1use std::collections::HashMap;
35use std::ffi::c_void;
36use std::sync::{Arc, Mutex};
37use std::time::{Duration, Instant};
38
39const SIM_ALLOC_ALIGN: usize = 256;
47
48fn sim_alloc(size: usize, align: usize) -> Result<*mut c_void, OneApiError> {
64 if !align.is_power_of_two() || align > SIM_ALLOC_ALIGN {
65 return Err(OneApiError::OutOfMemory(format!(
66 "unsupported allocation alignment {align}: this simulated backend supports \
67 power-of-two alignments up to {SIM_ALLOC_ALIGN} bytes"
68 )));
69 }
70 if size == 0 {
71 return Ok(SIM_ALLOC_ALIGN as *mut c_void);
72 }
73 let total = SIM_ALLOC_ALIGN.checked_add(size).ok_or_else(|| {
74 OneApiError::OutOfMemory(format!(
75 "{size}-byte request overflows the allocator's size limit"
76 ))
77 })?;
78 let layout = std::alloc::Layout::from_size_align(total, SIM_ALLOC_ALIGN)
82 .map_err(|e| OneApiError::OutOfMemory(format!("invalid allocation layout: {e}")))?;
83 let base = unsafe { std::alloc::alloc(layout) };
86 if base.is_null() {
87 return Err(OneApiError::OutOfMemory(format!(
88 "allocator returned null for a {size}-byte request"
89 )));
90 }
91 unsafe { (base as *mut usize).write(size) };
95 Ok(unsafe { base.add(SIM_ALLOC_ALIGN) } as *mut c_void)
99}
100
101fn sim_dealloc(ptr: *mut c_void) {
110 if ptr.is_null() || (ptr as usize) == SIM_ALLOC_ALIGN {
111 return;
112 }
113 let base = unsafe { (ptr as *mut u8).sub(SIM_ALLOC_ALIGN) };
115 let size = unsafe { (base as *const usize).read() };
117 if let Ok(layout) = std::alloc::Layout::from_size_align(SIM_ALLOC_ALIGN + size, SIM_ALLOC_ALIGN)
118 {
119 unsafe { std::alloc::dealloc(base, layout) };
122 }
123}
124
125pub struct OneApiMemoryBackend {
127 config: OneApiConfig,
129 device_properties: SyclDeviceProperties,
131 contexts: HashMap<u32, SyclContext>,
133 memory_pools: HashMap<OneApiMemoryType, OneApiMemoryPool>,
135 stats: OneApiStats,
137 queue_manager: SyclQueueManager,
139}
140
141#[derive(Debug, Clone)]
143pub struct OneApiConfig {
144 pub device_id: u32,
146 pub enable_usm: bool,
148 pub enable_device_usm: bool,
150 pub enable_host_usm: bool,
152 pub enable_shared_usm: bool,
154 pub enable_memory_pools: bool,
156 pub enable_async_ops: bool,
158 pub pool_growth_size: usize,
160 pub max_queues: u32,
162 pub enable_profiling: bool,
164 pub enable_sub_groups: bool,
166}
167
168impl Default for OneApiConfig {
169 fn default() -> Self {
170 Self {
171 device_id: 0,
172 enable_usm: true,
173 enable_device_usm: true,
174 enable_host_usm: true,
175 enable_shared_usm: true,
176 enable_memory_pools: true,
177 enable_async_ops: true,
178 pool_growth_size: 64 * 1024 * 1024, max_queues: 16,
180 enable_profiling: false,
181 enable_sub_groups: true,
182 }
183 }
184}
185
186#[derive(Debug, Clone)]
188pub struct SyclDeviceProperties {
189 pub device_id: u32,
190 pub name: String,
191 pub vendor: String,
192 pub device_type: SyclDeviceType,
193 pub driver_version: String,
194 pub global_memory_size: usize,
195 pub local_memory_size: usize,
196 pub max_work_group_size: u32,
197 pub max_work_item_dimensions: u32,
198 pub max_work_item_sizes: [u32; 3],
199 pub compute_units: u32,
200 pub max_compute_units: u32,
201 pub sub_group_sizes: Vec<u32>,
202 pub preferred_sub_group_size: u32,
203 pub max_constant_buffer_size: usize,
204 pub has_fp64: bool,
205 pub has_fp16: bool,
206 pub has_atomic64: bool,
207 pub usm_device_allocations: bool,
208 pub usm_host_allocations: bool,
209 pub usm_shared_allocations: bool,
210 pub usm_system_allocations: bool,
211}
212
213#[derive(Debug, Clone, PartialEq)]
215pub enum SyclDeviceType {
216 GPU,
217 CPU,
218 Accelerator,
219 Host,
220 Custom,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq, Hash)]
225pub enum OneApiMemoryType {
226 Device,
227 Host,
228 Shared,
229 System,
230 Buffer,
231}
232
233pub struct SyclContext {
235 pub handle: *mut c_void,
237 pub device_id: u32,
239 pub device_properties: SyclDeviceProperties,
241 pub created_at: Instant,
243 pub queues: Vec<SyclQueue>,
245 pub usm_allocations: HashMap<*mut c_void, UsmAllocation>,
247}
248
249#[derive(Debug, Clone)]
251pub struct UsmAllocation {
252 pub ptr: *mut c_void,
253 pub size: usize,
254 pub usm_kind: UsmKind,
255 pub allocated_at: Instant,
256 pub device_id: u32,
257 pub alignment: usize,
258}
259
260#[derive(Debug, Clone, PartialEq)]
262pub enum UsmKind {
263 Device, Host, Shared, System, }
268
269pub struct SyclQueue {
271 pub handle: *mut c_void,
273 pub id: u32,
275 pub properties: SyclQueueProperties,
277 pub created_at: Instant,
279 pub operations: std::collections::VecDeque<SyclOperation>,
281 pub context_id: Option<u32>,
283}
284
285#[derive(Debug, Clone)]
287pub struct SyclQueueProperties {
288 pub in_order: bool,
289 pub enable_profiling: bool,
290 pub priority: SyclQueuePriority,
291}
292
293impl Default for SyclQueueProperties {
294 fn default() -> Self {
295 Self {
296 in_order: false,
297 enable_profiling: false,
298 priority: SyclQueuePriority::Normal,
299 }
300 }
301}
302
303#[derive(Debug, Clone, PartialEq)]
305pub enum SyclQueuePriority {
306 Low,
307 Normal,
308 High,
309}
310
311#[derive(Debug, Clone)]
313pub struct SyclOperation {
314 pub op_type: SyclOperationType,
315 pub src_ptr: Option<*mut c_void>,
316 pub dst_ptr: Option<*mut c_void>,
317 pub size: usize,
318 pub timestamp: Instant,
319 pub event_handle: Option<*mut c_void>,
320}
321
322#[derive(Debug, Clone)]
324pub enum SyclOperationType {
325 MemcpyHostToDevice,
326 MemcpyDeviceToHost,
327 MemcpyDeviceToDevice,
328 UsmMemcpy,
329 UsmMemset,
330 KernelSubmit,
331 BarrierWait,
332 Fill,
333}
334
335pub struct OneApiMemoryPool {
337 memory_type: OneApiMemoryType,
339 current_size: usize,
341 max_size: usize,
343 used_size: usize,
345 free_blocks: std::collections::VecDeque<OneApiMemoryBlock>,
347 allocated_blocks: HashMap<*mut c_void, OneApiMemoryBlock>,
349 usm_properties: UsmProperties,
351}
352
353#[derive(Debug, Clone)]
355pub struct OneApiMemoryBlock {
356 pub ptr: *mut c_void,
357 pub size: usize,
358 pub memory_type: OneApiMemoryType,
359 pub allocated_at: Instant,
360 pub last_access: Option<Instant>,
361 pub ref_count: u32,
362 pub usm_kind: Option<UsmKind>,
363 pub device_accessible: bool,
364 pub host_accessible: bool,
365}
366
367#[derive(Debug, Clone)]
369pub struct UsmProperties {
370 pub alignment: usize,
371 pub device_read_only: bool,
372 pub device_access: bool,
373 pub host_access: bool,
374 pub supports_atomics: bool,
375}
376
377impl Default for UsmProperties {
378 fn default() -> Self {
379 Self {
380 alignment: 64, device_read_only: false,
382 device_access: true,
383 host_access: false,
384 supports_atomics: true,
385 }
386 }
387}
388
389impl OneApiMemoryPool {
390 pub fn new(memory_type: OneApiMemoryType, max_size: usize) -> Self {
391 let usm_properties = match memory_type {
392 OneApiMemoryType::Device => UsmProperties {
393 alignment: 64,
394 device_read_only: false,
395 device_access: true,
396 host_access: false,
397 supports_atomics: true,
398 },
399 OneApiMemoryType::Host => UsmProperties {
400 alignment: 64,
401 device_read_only: false,
402 device_access: true,
403 host_access: true,
404 supports_atomics: false,
405 },
406 OneApiMemoryType::Shared => UsmProperties {
407 alignment: 64,
408 device_read_only: false,
409 device_access: true,
410 host_access: true,
411 supports_atomics: true,
412 },
413 _ => UsmProperties::default(),
414 };
415
416 Self {
417 memory_type,
418 current_size: 0,
419 max_size,
420 used_size: 0,
421 free_blocks: std::collections::VecDeque::new(),
422 allocated_blocks: HashMap::new(),
423 usm_properties,
424 }
425 }
426
427 pub fn allocate(&mut self, size: usize) -> Result<*mut c_void, OneApiError> {
429 for i in 0..self.free_blocks.len() {
431 if self.free_blocks[i].size >= size {
432 let Some(mut block) = self.free_blocks.remove(i) else {
433 continue;
434 };
435
436 if block.size > size * 2 {
438 let remaining_block = OneApiMemoryBlock {
439 ptr: unsafe { block.ptr.add(size) },
440 size: block.size - size,
441 memory_type: block.memory_type.clone(),
442 allocated_at: block.allocated_at,
443 last_access: None,
444 ref_count: 0,
445 usm_kind: block.usm_kind.clone(),
446 device_accessible: block.device_accessible,
447 host_accessible: block.host_accessible,
448 };
449 self.free_blocks.push_back(remaining_block);
450 block.size = size;
451 }
452
453 block.last_access = Some(Instant::now());
454 block.ref_count = 1;
455
456 let ptr = block.ptr;
457 self.allocated_blocks.insert(ptr, block);
458 self.used_size += size;
459
460 return Ok(ptr);
461 }
462 }
463
464 if self.current_size + size > self.max_size {
466 return Err(OneApiError::OutOfMemory(
467 "Pool size limit exceeded".to_string(),
468 ));
469 }
470
471 let ptr = self.sycl_malloc(size)?;
472 let usm_kind = match self.memory_type {
473 OneApiMemoryType::Device => Some(UsmKind::Device),
474 OneApiMemoryType::Host => Some(UsmKind::Host),
475 OneApiMemoryType::Shared => Some(UsmKind::Shared),
476 OneApiMemoryType::System => Some(UsmKind::System),
477 _ => None,
478 };
479
480 let block = OneApiMemoryBlock {
481 ptr,
482 size,
483 memory_type: self.memory_type.clone(),
484 allocated_at: Instant::now(),
485 last_access: Some(Instant::now()),
486 ref_count: 1,
487 usm_kind,
488 device_accessible: self.usm_properties.device_access,
489 host_accessible: self.usm_properties.host_access,
490 };
491
492 self.allocated_blocks.insert(ptr, block);
493 self.current_size += size;
494 self.used_size += size;
495
496 Ok(ptr)
497 }
498
499 pub fn free(&mut self, ptr: *mut c_void) -> Result<(), OneApiError> {
501 if let Some(block) = self.allocated_blocks.remove(&ptr) {
502 self.used_size -= block.size;
503
504 self.free_blocks.push_back(OneApiMemoryBlock {
506 ptr: block.ptr,
507 size: block.size,
508 memory_type: block.memory_type,
509 allocated_at: block.allocated_at,
510 last_access: None,
511 ref_count: 0,
512 usm_kind: block.usm_kind,
513 device_accessible: block.device_accessible,
514 host_accessible: block.host_accessible,
515 });
516
517 self.coalesce_free_blocks();
519
520 Ok(())
521 } else {
522 Err(OneApiError::InvalidPointer(
523 "Pointer not found in pool".to_string(),
524 ))
525 }
526 }
527
528 fn coalesce_free_blocks(&mut self) {
529 let mut blocks: Vec<OneApiMemoryBlock> = self.free_blocks.drain(..).collect();
531 blocks.sort_by_key(|block| block.ptr as usize);
532
533 let mut coalesced = Vec::new();
534 let mut current_block: Option<OneApiMemoryBlock> = None;
535
536 for block in blocks {
537 match current_block.take() {
538 None => current_block = Some(block),
539 Some(mut prev_block) => {
540 let prev_end = prev_block.ptr as usize + prev_block.size;
541 let block_start = block.ptr as usize;
542
543 if prev_end == block_start && prev_block.memory_type == block.memory_type {
544 prev_block.size += block.size;
546 current_block = Some(prev_block);
547 } else {
548 coalesced.push(prev_block);
549 current_block = Some(block);
550 }
551 }
552 }
553 }
554
555 if let Some(block) = current_block {
556 coalesced.push(block);
557 }
558
559 self.free_blocks = coalesced.into();
560 }
561
562 fn sycl_malloc(&self, size: usize) -> Result<*mut c_void, OneApiError> {
563 match self.memory_type {
565 OneApiMemoryType::Device => sim_alloc(size, self.usm_properties.alignment), OneApiMemoryType::Host => sim_alloc(size, self.usm_properties.alignment), OneApiMemoryType::Shared => sim_alloc(size, self.usm_properties.alignment), OneApiMemoryType::System => sim_alloc(size, self.usm_properties.alignment), _ => Err(OneApiError::UnsupportedOperation(
570 "Unsupported memory type for allocation".to_string(),
571 )),
572 }
573 }
574}
575
576pub struct SyclQueueManager {
578 queues: Vec<SyclQueue>,
580 queue_pool: std::collections::VecDeque<SyclQueue>,
582 next_queue_id: u32,
584 config: SyclQueueConfig,
586}
587
588#[derive(Debug, Clone)]
590pub struct SyclQueueConfig {
591 pub default_priority: SyclQueuePriority,
592 pub enable_priorities: bool,
593 pub max_operations_per_queue: usize,
594 pub default_in_order: bool,
595}
596
597impl Default for SyclQueueConfig {
598 fn default() -> Self {
599 Self {
600 default_priority: SyclQueuePriority::Normal,
601 enable_priorities: true,
602 max_operations_per_queue: 1000,
603 default_in_order: false,
604 }
605 }
606}
607
608impl SyclQueueManager {
609 pub fn new(config: SyclQueueConfig) -> Self {
610 Self {
611 queues: Vec::new(),
612 queue_pool: std::collections::VecDeque::new(),
613 next_queue_id: 0,
614 config,
615 }
616 }
617
618 pub fn create_queue(
624 &mut self,
625 properties: Option<SyclQueueProperties>,
626 ) -> Result<u32, OneApiError> {
627 let queue_id = self.next_queue_id;
628 self.next_queue_id += 1;
629
630 let queue_properties = properties.unwrap_or_else(|| SyclQueueProperties {
631 in_order: self.config.default_in_order,
632 enable_profiling: false,
633 priority: self.config.default_priority.clone(),
634 });
635
636 let mut queue = self.queue_pool.pop_front().unwrap_or_else(|| SyclQueue {
637 handle: std::ptr::null_mut(), id: queue_id,
639 properties: queue_properties.clone(),
640 created_at: Instant::now(),
641 operations: std::collections::VecDeque::new(),
642 context_id: None,
643 });
644 queue.id = queue_id;
645 queue.properties = queue_properties;
646 queue.created_at = Instant::now();
647 queue.operations.clear();
648 queue.context_id = None;
649
650 self.queues.push(queue);
651 Ok(queue_id)
652 }
653
654 pub fn destroy_queue(&mut self, queue_id: u32) -> Result<(), OneApiError> {
659 if let Some(pos) = self.queues.iter().position(|q| q.id == queue_id) {
660 let queue = self.queues.remove(pos);
661 self.queue_pool.push_back(queue);
662 Ok(())
663 } else {
664 Err(OneApiError::InvalidQueue("Queue not found".to_string()))
665 }
666 }
667
668 pub fn submit_operation(
670 &mut self,
671 queue_id: u32,
672 operation: SyclOperation,
673 ) -> Result<(), OneApiError> {
674 if let Some(queue) = self.queues.iter_mut().find(|q| q.id == queue_id) {
675 if queue.operations.len() >= self.config.max_operations_per_queue {
676 return Err(OneApiError::QueueFull(
677 "Queue operation limit reached".to_string(),
678 ));
679 }
680
681 queue.operations.push_back(operation);
682 Ok(())
683 } else {
684 Err(OneApiError::InvalidQueue("Queue not found".to_string()))
685 }
686 }
687
688 pub fn wait_for_queue(&mut self, queue_id: u32) -> Result<(), OneApiError> {
690 let mut operations = Vec::new();
692 if let Some(queue) = self.queues.iter_mut().find(|q| q.id == queue_id) {
693 while let Some(operation) = queue.operations.pop_front() {
694 operations.push(operation);
695 }
696 } else {
697 return Err(OneApiError::InvalidQueue("Queue not found".to_string()));
698 }
699
700 for operation in operations {
702 self.execute_operation(operation)?;
703 }
704
705 Ok(())
706 }
707
708 fn execute_operation(&self, _operation: SyclOperation) -> Result<(), OneApiError> {
709 Ok(())
718 }
719}
720
721#[derive(Debug, Clone, Default)]
723pub struct OneApiStats {
724 pub total_allocations: u64,
725 pub total_deallocations: u64,
726 pub bytes_allocated: u64,
727 pub bytes_deallocated: u64,
728 pub device_memory_used: usize,
729 pub host_memory_used: usize,
730 pub shared_memory_used: usize,
731 pub usm_allocations: u64,
732 pub queue_operations: u64,
733 pub kernel_submissions: u64,
734 pub memory_transfers: u64,
735 pub average_allocation_time: Duration,
736 pub peak_memory_usage: usize,
737}
738
739impl OneApiMemoryBackend {
740 pub fn new(config: OneApiConfig) -> Result<Self, OneApiError> {
742 let device_properties = Self::query_device_properties(config.device_id)?;
744
745 let mut memory_pools = HashMap::new();
747 if config.enable_memory_pools {
748 let pool_size = device_properties.global_memory_size / 4; if config.enable_device_usm {
751 memory_pools.insert(
752 OneApiMemoryType::Device,
753 OneApiMemoryPool::new(OneApiMemoryType::Device, pool_size),
754 );
755 }
756
757 if config.enable_host_usm {
758 memory_pools.insert(
759 OneApiMemoryType::Host,
760 OneApiMemoryPool::new(OneApiMemoryType::Host, pool_size),
761 );
762 }
763
764 if config.enable_shared_usm {
765 memory_pools.insert(
766 OneApiMemoryType::Shared,
767 OneApiMemoryPool::new(OneApiMemoryType::Shared, pool_size / 2),
768 );
769 }
770
771 memory_pools.insert(
772 OneApiMemoryType::System,
773 OneApiMemoryPool::new(OneApiMemoryType::System, pool_size / 4),
774 );
775 }
776
777 let queue_manager = SyclQueueManager::new(SyclQueueConfig::default());
778
779 Ok(Self {
780 config,
781 device_properties,
782 contexts: HashMap::new(),
783 memory_pools,
784 stats: OneApiStats::default(),
785 queue_manager,
786 })
787 }
788
789 fn query_device_properties(device_id: u32) -> Result<SyclDeviceProperties, OneApiError> {
791 Ok(SyclDeviceProperties {
793 device_id,
794 name: format!("Intel GPU {}", device_id),
795 vendor: "Intel Corporation".to_string(),
796 device_type: SyclDeviceType::GPU,
797 driver_version: "1.3.0".to_string(),
798 global_memory_size: 12 * 1024 * 1024 * 1024, local_memory_size: 64 * 1024, max_work_group_size: 1024,
801 max_work_item_dimensions: 3,
802 max_work_item_sizes: [1024, 1024, 1024],
803 compute_units: 96,
804 max_compute_units: 96,
805 sub_group_sizes: vec![8, 16, 32],
806 preferred_sub_group_size: 16,
807 max_constant_buffer_size: 64 * 1024,
808 has_fp64: true,
809 has_fp16: true,
810 has_atomic64: true,
811 usm_device_allocations: true,
812 usm_host_allocations: true,
813 usm_shared_allocations: true,
814 usm_system_allocations: true,
815 })
816 }
817
818 pub fn allocate(
820 &mut self,
821 size: usize,
822 memory_type: OneApiMemoryType,
823 ) -> Result<*mut c_void, OneApiError> {
824 let start_time = Instant::now();
825
826 let ptr = if self.config.enable_memory_pools {
827 if let Some(pool) = self.memory_pools.get_mut(&memory_type) {
828 pool.allocate(size)?
829 } else {
830 return Err(OneApiError::UnsupportedMemoryType(
831 "Memory type not supported".to_string(),
832 ));
833 }
834 } else {
835 self.direct_allocate(size, memory_type.clone())?
837 };
838
839 self.stats.total_allocations += 1;
841 self.stats.bytes_allocated += size as u64;
842
843 match memory_type {
844 OneApiMemoryType::Device => self.stats.device_memory_used += size,
845 OneApiMemoryType::Host => self.stats.host_memory_used += size,
846 OneApiMemoryType::Shared => self.stats.shared_memory_used += size,
847 _ => {}
848 }
849
850 if matches!(
851 memory_type,
852 OneApiMemoryType::Device | OneApiMemoryType::Host | OneApiMemoryType::Shared
853 ) {
854 self.stats.usm_allocations += 1;
855 }
856
857 let allocation_time = start_time.elapsed();
858 let total_time = self.stats.average_allocation_time.as_nanos() as u64
859 * (self.stats.total_allocations - 1)
860 + allocation_time.as_nanos() as u64;
861 self.stats.average_allocation_time =
862 Duration::from_nanos(total_time / self.stats.total_allocations);
863
864 let current_usage = self.stats.device_memory_used
865 + self.stats.host_memory_used
866 + self.stats.shared_memory_used;
867 if current_usage > self.stats.peak_memory_usage {
868 self.stats.peak_memory_usage = current_usage;
869 }
870
871 Ok(ptr)
872 }
873
874 fn direct_allocate(
875 &self,
876 size: usize,
877 memory_type: OneApiMemoryType,
878 ) -> Result<*mut c_void, OneApiError> {
879 let alignment = 64; match memory_type {
883 OneApiMemoryType::Device => sim_alloc(size, alignment), OneApiMemoryType::Host => sim_alloc(size, alignment), OneApiMemoryType::Shared => sim_alloc(size, alignment), OneApiMemoryType::System => sim_alloc(size, alignment), _ => Err(OneApiError::UnsupportedMemoryType(
888 "Unsupported memory type".to_string(),
889 )),
890 }
891 }
892
893 pub fn free(
895 &mut self,
896 ptr: *mut c_void,
897 memory_type: OneApiMemoryType,
898 ) -> Result<(), OneApiError> {
899 if self.config.enable_memory_pools {
900 if let Some(pool) = self.memory_pools.get_mut(&memory_type) {
901 pool.free(ptr)?;
902 } else {
903 return Err(OneApiError::UnsupportedMemoryType(
904 "Memory type not supported".to_string(),
905 ));
906 }
907 } else {
908 sim_dealloc(ptr);
912 }
913
914 self.stats.total_deallocations += 1;
915 Ok(())
916 }
917
918 pub fn usm_memcpy(
920 &mut self,
921 dst: *mut c_void,
922 src: *const c_void,
923 size: usize,
924 queue_id: u32,
925 ) -> Result<(), OneApiError> {
926 let operation = SyclOperation {
927 op_type: SyclOperationType::UsmMemcpy,
928 src_ptr: Some(src as *mut c_void),
929 dst_ptr: Some(dst),
930 size,
931 timestamp: Instant::now(),
932 event_handle: None,
933 };
934
935 self.queue_manager.submit_operation(queue_id, operation)?;
936 self.stats.memory_transfers += 1;
937 Ok(())
938 }
939
940 pub fn create_context(&mut self) -> Result<u32, OneApiError> {
942 let context_id = self.contexts.len() as u32;
943
944 let context = SyclContext {
945 handle: std::ptr::null_mut(), device_id: self.config.device_id,
947 device_properties: self.device_properties.clone(),
948 created_at: Instant::now(),
949 queues: Vec::new(),
950 usm_allocations: HashMap::new(),
951 };
952
953 self.contexts.insert(context_id, context);
954 Ok(context_id)
955 }
956
957 pub fn create_queue(
959 &mut self,
960 properties: Option<SyclQueueProperties>,
961 ) -> Result<u32, OneApiError> {
962 self.queue_manager.create_queue(properties)
963 }
964
965 pub fn destroy_queue(&mut self, queue_id: u32) -> Result<(), OneApiError> {
967 self.queue_manager.destroy_queue(queue_id)
968 }
969
970 pub fn wait_all(&mut self) -> Result<(), OneApiError> {
972 let queue_ids: Vec<u32> = self.queue_manager.queues.iter().map(|q| q.id).collect();
973 for queue_id in queue_ids {
974 self.queue_manager.wait_for_queue(queue_id)?;
975 }
976 Ok(())
977 }
978
979 pub fn get_device_properties(&self) -> &SyclDeviceProperties {
981 &self.device_properties
982 }
983
984 pub fn get_stats(&self) -> &OneApiStats {
986 &self.stats
987 }
988
989 pub fn query_usm_ptr(&self, ptr: *mut c_void) -> Result<UsmAllocation, OneApiError> {
991 Ok(UsmAllocation {
994 ptr,
995 size: 0, usm_kind: UsmKind::Device,
997 allocated_at: Instant::now(),
998 device_id: self.config.device_id,
999 alignment: 64,
1000 })
1001 }
1002}
1003
1004unsafe impl Send for OneApiMemoryBackend {}
1011unsafe impl Sync for OneApiMemoryBackend {}
1012
1013#[derive(Debug, Clone)]
1015pub enum OneApiError {
1016 DeviceNotFound(String),
1017 OutOfMemory(String),
1018 InvalidPointer(String),
1019 InvalidQueue(String),
1020 QueueFull(String),
1021 UnsupportedOperation(String),
1022 UnsupportedMemoryType(String),
1023 ContextCreationFailed(String),
1024 KernelSubmissionFailed(String),
1025 SynchronizationFailed(String),
1026 InternalError(String),
1027}
1028
1029impl std::fmt::Display for OneApiError {
1030 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1031 match self {
1032 OneApiError::DeviceNotFound(msg) => write!(f, "Device not found: {}", msg),
1033 OneApiError::OutOfMemory(msg) => write!(f, "Out of memory: {}", msg),
1034 OneApiError::InvalidPointer(msg) => write!(f, "Invalid pointer: {}", msg),
1035 OneApiError::InvalidQueue(msg) => write!(f, "Invalid queue: {}", msg),
1036 OneApiError::QueueFull(msg) => write!(f, "Queue full: {}", msg),
1037 OneApiError::UnsupportedOperation(msg) => write!(f, "Unsupported operation: {}", msg),
1038 OneApiError::UnsupportedMemoryType(msg) => {
1039 write!(f, "Unsupported memory type: {}", msg)
1040 }
1041 OneApiError::ContextCreationFailed(msg) => {
1042 write!(f, "Context creation failed: {}", msg)
1043 }
1044 OneApiError::KernelSubmissionFailed(msg) => {
1045 write!(f, "Kernel submission failed: {}", msg)
1046 }
1047 OneApiError::SynchronizationFailed(msg) => write!(f, "Synchronization failed: {}", msg),
1048 OneApiError::InternalError(msg) => write!(f, "Internal error: {}", msg),
1049 }
1050 }
1051}
1052
1053impl std::error::Error for OneApiError {}
1054
1055pub struct ThreadSafeOneApiBackend {
1057 backend: Arc<Mutex<OneApiMemoryBackend>>,
1058}
1059
1060impl ThreadSafeOneApiBackend {
1061 pub fn new(config: OneApiConfig) -> Result<Self, OneApiError> {
1062 let backend = OneApiMemoryBackend::new(config)?;
1063 Ok(Self {
1064 backend: Arc::new(Mutex::new(backend)),
1065 })
1066 }
1067
1068 pub fn allocate(
1069 &self,
1070 size: usize,
1071 memory_type: OneApiMemoryType,
1072 ) -> Result<*mut c_void, OneApiError> {
1073 let mut backend = self.backend.lock().unwrap_or_else(|e| e.into_inner());
1074 backend.allocate(size, memory_type)
1075 }
1076
1077 pub fn free(&self, ptr: *mut c_void, memory_type: OneApiMemoryType) -> Result<(), OneApiError> {
1078 let mut backend = self.backend.lock().unwrap_or_else(|e| e.into_inner());
1079 backend.free(ptr, memory_type)
1080 }
1081
1082 pub fn get_stats(&self) -> OneApiStats {
1083 let backend = self.backend.lock().unwrap_or_else(|e| e.into_inner());
1084 backend.get_stats().clone()
1085 }
1086}
1087
1088#[cfg(test)]
1089mod tests {
1090 use super::*;
1091
1092 #[test]
1096 fn sim_alloc_zero_size_is_a_safe_sentinel_not_a_ub_call() {
1097 let ptr = sim_alloc(0, 64).expect("zero-size request must succeed");
1098 assert!(!ptr.is_null());
1099 sim_dealloc(ptr);
1100 }
1101
1102 #[test]
1103 fn sim_alloc_rejects_unsupported_alignment() {
1104 assert!(sim_alloc(16, 3).is_err(), "3 is not a power of two");
1105 assert!(
1106 sim_alloc(16, 512).is_err(),
1107 "512 exceeds this simulated backend's supported alignment"
1108 );
1109 }
1110
1111 #[test]
1114 fn sim_alloc_real_allocation_round_trips_and_frees_cleanly() {
1115 for (size, align) in [(1usize, 8usize), (7, 16), (256, 64), (4096, 128)] {
1116 let ptr = sim_alloc(size, align).expect("allocation must succeed") as *mut u8;
1117 assert!(!ptr.is_null());
1118 assert_eq!(
1119 (ptr as usize) % align,
1120 0,
1121 "returned pointer does not honour the requested alignment"
1122 );
1123 unsafe {
1124 for i in 0..size {
1125 ptr.add(i).write(0xAB);
1126 }
1127 for i in 0..size {
1128 assert_eq!(ptr.add(i).read(), 0xAB);
1129 }
1130 sim_dealloc(ptr as *mut c_void);
1131 }
1132 }
1133 }
1134
1135 #[test]
1136 fn sim_dealloc_null_is_a_no_op() {
1137 sim_dealloc(std::ptr::null_mut());
1138 }
1139
1140 #[test]
1141 fn test_oneapi_backend_creation() {
1142 let config = OneApiConfig::default();
1143 let backend = OneApiMemoryBackend::new(config);
1144 assert!(backend.is_ok());
1145 }
1146
1147 #[test]
1148 fn test_memory_pool() {
1149 let mut pool = OneApiMemoryPool::new(OneApiMemoryType::Device, 1024 * 1024);
1150 let ptr = pool.allocate(1024);
1151 assert!(ptr.is_ok());
1152
1153 let ptr = ptr.expect("unwrap failed");
1154 let result = pool.free(ptr);
1155 assert!(result.is_ok());
1156 }
1157
1158 #[test]
1159 fn test_sycl_queue_manager() {
1160 let mut manager = SyclQueueManager::new(SyclQueueConfig::default());
1161 let queue_id = manager.create_queue(None);
1162 assert!(queue_id.is_ok());
1163
1164 let queue_id = queue_id.expect("unwrap failed");
1165 let result = manager.destroy_queue(queue_id);
1166 assert!(result.is_ok());
1167 }
1168
1169 #[test]
1170 fn test_thread_safe_backend() {
1171 let config = OneApiConfig::default();
1172 let backend = ThreadSafeOneApiBackend::new(config);
1173 assert!(backend.is_ok());
1174
1175 let backend = backend.expect("unwrap failed");
1176 let stats = backend.get_stats();
1177 assert_eq!(stats.total_allocations, 0);
1178 }
1179}