Skip to main content

optirs_gpu/memory/allocation/
mod.rs

1// GPU memory allocation strategies and algorithms
2//
3// This module provides various memory allocation strategies optimized for
4// different GPU workload patterns and memory usage scenarios.
5
6pub mod arena_allocator;
7pub mod buddy_allocator;
8pub mod slab_allocator;
9pub mod strategies;
10
11// Re-export main types for convenience
12pub use strategies::{
13    AdaptiveConfig, AllocationEvent, AllocationPattern, AllocationStats, AllocationStrategy,
14    AllocationStrategyManager, HybridConfig, MLConfig, MLFeatures, MLPrediction, MemoryBlock,
15};
16
17pub use buddy_allocator::{
18    AllocationInfo, BuddyAllocator, BuddyBlock, BuddyConfig, BuddyError, BuddyStats,
19    FreeBlockStats, MemoryUsage, ThreadSafeBuddyAllocator,
20};
21
22pub use slab_allocator::{
23    CacheConfig, CacheInfo, MemoryPool, MemoryPoolUsage, Slab, SlabAllocator, SlabAllocatorStats,
24    SlabCache, SlabConfig, SlabError, ThreadSafeSlabAllocator,
25};
26
27pub use arena_allocator::{
28    ArenaAllocator, ArenaConfig, ArenaError, ArenaStats, ArenaUsage, CheckpointHandle,
29    ExternalAllocator, GrowingArena, MemoryLayout, MemoryRegion, RingArena, RingConfig, RingUsage,
30    ThreadSafeArena,
31};
32
33use std::collections::HashMap;
34use std::ptr::NonNull;
35use std::sync::{Arc, Mutex};
36use std::time::Instant;
37
38/// Unified allocator interface that can use different allocation strategies
39pub struct UnifiedAllocator {
40    /// Strategy manager for general allocations
41    strategy_manager: AllocationStrategyManager,
42    /// Buddy allocator for power-of-2 allocations
43    buddy_allocator: Option<BuddyAllocator>,
44    /// Slab allocator for fixed-size objects
45    slab_allocator: Option<SlabAllocator>,
46    /// Arena allocator for temporary allocations
47    arena_allocator: Option<ArenaAllocator>,
48    /// Configuration
49    config: UnifiedConfig,
50    /// Statistics
51    stats: UnifiedStats,
52    /// Allocation routing table
53    routing_table: AllocationRouter,
54}
55
56/// Configuration for unified allocator
57#[derive(Debug, Clone)]
58pub struct UnifiedConfig {
59    /// Default allocation strategy
60    pub default_strategy: AllocationStrategy,
61    /// Enable buddy allocator
62    pub enable_buddy: bool,
63    /// Enable slab allocator
64    pub enable_slab: bool,
65    /// Enable arena allocator
66    pub enable_arena: bool,
67    /// Size threshold for buddy allocator
68    pub buddy_threshold: usize,
69    /// Size threshold for slab allocator
70    pub slab_threshold: usize,
71    /// Size threshold for arena allocator
72    pub arena_threshold: usize,
73    /// Enable automatic routing optimization
74    pub enable_auto_routing: bool,
75    /// Statistics collection interval
76    pub stats_interval: std::time::Duration,
77}
78
79impl Default for UnifiedConfig {
80    fn default() -> Self {
81        Self {
82            default_strategy: AllocationStrategy::Adaptive,
83            enable_buddy: true,
84            enable_slab: true,
85            enable_arena: true,
86            buddy_threshold: 1024,
87            slab_threshold: 4096,
88            arena_threshold: 64 * 1024,
89            enable_auto_routing: true,
90            stats_interval: std::time::Duration::from_secs(1),
91        }
92    }
93}
94
95/// Unified allocator statistics
96#[derive(Debug, Clone, Default)]
97pub struct UnifiedStats {
98    pub total_allocations: u64,
99    pub total_deallocations: u64,
100    pub bytes_allocated: u64,
101    pub bytes_deallocated: u64,
102    pub strategy_allocations: HashMap<AllocationStrategy, u64>,
103    pub buddy_allocations: u64,
104    pub slab_allocations: u64,
105    pub arena_allocations: u64,
106    pub routing_decisions: u64,
107    pub routing_cache_hits: u64,
108    pub average_allocation_time_ns: f64,
109    pub peak_memory_usage: usize,
110    pub current_memory_usage: usize,
111}
112
113/// Allocation routing logic
114pub struct AllocationRouter {
115    /// Size-based routing rules
116    size_routes: Vec<SizeRoute>,
117    /// Pattern-based routing cache
118    pattern_cache: HashMap<AllocationPattern, AllocatorType>,
119    /// Performance history for routing decisions
120    performance_history: HashMap<AllocatorType, PerformanceMetrics>,
121    /// Configuration
122    config: RouterConfig,
123}
124
125/// Size-based routing rule
126#[derive(Debug, Clone)]
127pub struct SizeRoute {
128    pub min_size: usize,
129    pub max_size: Option<usize>,
130    pub preferred_allocator: AllocatorType,
131    pub fallback_allocator: Option<AllocatorType>,
132}
133
134/// Allocator type identification
135#[derive(Debug, Clone, PartialEq, Eq, Hash)]
136pub enum AllocatorType {
137    Strategy(AllocationStrategy),
138    Buddy,
139    Slab,
140    Arena,
141}
142
143/// Performance metrics for routing decisions
144#[derive(Debug, Clone, Default)]
145pub struct PerformanceMetrics {
146    pub average_latency_ns: f64,
147    pub success_rate: f64,
148    pub fragmentation_ratio: f64,
149    pub cache_hit_rate: f64,
150    pub memory_efficiency: f64,
151}
152
153/// Router configuration
154#[derive(Debug, Clone)]
155pub struct RouterConfig {
156    pub enable_performance_tracking: bool,
157    pub cache_size: usize,
158    pub adaptation_threshold: f64,
159    pub performance_window: usize,
160}
161
162impl Default for RouterConfig {
163    fn default() -> Self {
164        Self {
165            enable_performance_tracking: true,
166            cache_size: 1000,
167            adaptation_threshold: 0.1,
168            performance_window: 100,
169        }
170    }
171}
172
173impl AllocationRouter {
174    pub fn new(config: RouterConfig) -> Self {
175        let size_routes = vec![
176            SizeRoute {
177                min_size: 0,
178                max_size: Some(256),
179                preferred_allocator: AllocatorType::Slab,
180                fallback_allocator: Some(AllocatorType::Strategy(AllocationStrategy::FirstFit)),
181            },
182            SizeRoute {
183                min_size: 257,
184                max_size: Some(4096),
185                preferred_allocator: AllocatorType::Strategy(AllocationStrategy::BestFit),
186                fallback_allocator: Some(AllocatorType::Buddy),
187            },
188            SizeRoute {
189                min_size: 4097,
190                max_size: Some(64 * 1024),
191                preferred_allocator: AllocatorType::Buddy,
192                fallback_allocator: Some(AllocatorType::Strategy(AllocationStrategy::BestFit)),
193            },
194            SizeRoute {
195                min_size: 64 * 1024 + 1,
196                max_size: None,
197                preferred_allocator: AllocatorType::Arena,
198                fallback_allocator: Some(AllocatorType::Strategy(AllocationStrategy::WorstFit)),
199            },
200        ];
201
202        Self {
203            size_routes,
204            pattern_cache: HashMap::new(),
205            performance_history: HashMap::new(),
206            config,
207        }
208    }
209
210    /// Route allocation request to appropriate allocator
211    pub fn route_allocation(
212        &mut self,
213        size: usize,
214        pattern: Option<AllocationPattern>,
215    ) -> AllocatorType {
216        // Check pattern cache first
217        if let Some(pattern) = pattern {
218            if let Some(allocator_type) = self.pattern_cache.get(&pattern) {
219                return allocator_type.clone();
220            }
221        }
222
223        // Use size-based routing
224        for route in &self.size_routes {
225            if size >= route.min_size && route.max_size.is_none_or(|max| size <= max) {
226                // Check performance if tracking is enabled
227                if self.config.enable_performance_tracking {
228                    let preferred_perf = self
229                        .performance_history
230                        .get(&route.preferred_allocator)
231                        .cloned()
232                        .unwrap_or_default();
233
234                    if let Some(fallback) = &route.fallback_allocator {
235                        let fallback_perf = self
236                            .performance_history
237                            .get(fallback)
238                            .cloned()
239                            .unwrap_or_default();
240
241                        // Choose based on performance
242                        if fallback_perf.average_latency_ns > 0.0
243                            && preferred_perf.average_latency_ns > 0.0
244                        {
245                            let perf_ratio = fallback_perf.average_latency_ns
246                                / preferred_perf.average_latency_ns;
247                            if perf_ratio < 1.0 - self.config.adaptation_threshold {
248                                return fallback.clone();
249                            }
250                        }
251                    }
252                }
253
254                return route.preferred_allocator.clone();
255            }
256        }
257
258        // Default fallback
259        AllocatorType::Strategy(AllocationStrategy::BestFit)
260    }
261
262    /// Update performance metrics for an allocator
263    pub fn update_performance(
264        &mut self,
265        allocator_type: AllocatorType,
266        metrics: PerformanceMetrics,
267    ) {
268        self.performance_history.insert(allocator_type, metrics);
269    }
270
271    /// Cache pattern-based routing decision
272    pub fn cache_pattern_route(
273        &mut self,
274        pattern: AllocationPattern,
275        allocator_type: AllocatorType,
276    ) {
277        if self.pattern_cache.len() >= self.config.cache_size {
278            // Remove oldest entry (simplified - could use LRU)
279            if let Some(key) = self.pattern_cache.keys().next().cloned() {
280                self.pattern_cache.remove(&key);
281            }
282        }
283        self.pattern_cache.insert(pattern, allocator_type);
284    }
285}
286
287impl UnifiedAllocator {
288    /// Create a new unified allocator
289    pub fn new(
290        base_ptr: NonNull<u8>,
291        total_size: usize,
292        config: UnifiedConfig,
293    ) -> Result<Self, AllocationError> {
294        let mut strategy_manager = AllocationStrategyManager::new(config.default_strategy.clone());
295
296        let buddy_allocator = if config.enable_buddy {
297            let buddy_config = BuddyConfig::default();
298            let buddy_size = total_size / 4; // Allocate 1/4 of memory to buddy allocator
299            let buddy_ptr = base_ptr;
300            Some(BuddyAllocator::new(
301                buddy_ptr.as_ptr(),
302                buddy_size,
303                buddy_config,
304            )?)
305        } else {
306            None
307        };
308
309        let slab_allocator = if config.enable_slab {
310            let slab_config = SlabConfig::default();
311            let slab_size = total_size / 4; // Allocate 1/4 of memory to slab allocator
312            let slab_ptr = unsafe { NonNull::new_unchecked(base_ptr.as_ptr().add(total_size / 4)) };
313            Some(SlabAllocator::new(slab_ptr, slab_size, slab_config))
314        } else {
315            None
316        };
317
318        let arena_allocator = if config.enable_arena {
319            let arena_config = ArenaConfig::default();
320            let arena_size = total_size / 4; // Allocate 1/4 of memory to arena allocator
321            let arena_ptr =
322                unsafe { NonNull::new_unchecked(base_ptr.as_ptr().add(total_size / 2)) };
323            Some(ArenaAllocator::new(arena_ptr, arena_size, arena_config)?)
324        } else {
325            None
326        };
327
328        let routing_table = AllocationRouter::new(RouterConfig::default());
329
330        // Initialize strategy manager with remaining memory (1/4 of total)
331        let strategy_size = total_size / 4;
332        let strategy_ptr = unsafe { base_ptr.as_ptr().add(3 * total_size / 4) };
333        strategy_manager.add_free_block(MemoryBlock {
334            ptr: strategy_ptr,
335            size: strategy_size,
336            is_free: true,
337            allocated_at: None,
338            last_accessed: None,
339            access_count: 0,
340            fragmentation_score: 0.0,
341        });
342
343        Ok(Self {
344            strategy_manager,
345            buddy_allocator,
346            slab_allocator,
347            arena_allocator,
348            config,
349            stats: UnifiedStats::default(),
350            routing_table,
351        })
352    }
353
354    /// Allocate memory using the unified interface
355    pub fn allocate(
356        &mut self,
357        size: usize,
358        requested_allocator_type: AllocatorType,
359        _alignment: Option<usize>,
360    ) -> Result<NonNull<u8>, AllocationError> {
361        let start_time = Instant::now();
362        self.stats.total_allocations += 1;
363
364        // Use requested allocator type if provided, otherwise route automatically
365        let allocator_type = requested_allocator_type;
366
367        let result = match &allocator_type {
368            AllocatorType::Strategy(strategy) => {
369                self.strategy_manager.set_strategy(strategy.clone());
370                self.strategy_manager.find_free_block(size).ok_or_else(|| {
371                    AllocationError::OutOfMemory("Strategy allocator failed".to_string())
372                })
373            }
374            AllocatorType::Buddy => {
375                if let Some(ref mut buddy) = self.buddy_allocator {
376                    buddy.allocate(size).map_err(AllocationError::BuddyError)
377                } else {
378                    Err(AllocationError::AllocatorNotAvailable(
379                        "Buddy allocator not enabled".to_string(),
380                    ))
381                }
382            }
383            AllocatorType::Slab => {
384                if let Some(ref mut slab) = self.slab_allocator {
385                    slab.allocate(size)
386                        .map(|ptr| ptr.as_ptr())
387                        .map_err(AllocationError::SlabError)
388                } else {
389                    Err(AllocationError::AllocatorNotAvailable(
390                        "Slab allocator not enabled".to_string(),
391                    ))
392                }
393            }
394            AllocatorType::Arena => {
395                if let Some(ref mut arena) = self.arena_allocator {
396                    arena
397                        .allocate(size)
398                        .map(|ptr| ptr.as_ptr())
399                        .map_err(AllocationError::ArenaError)
400                } else {
401                    Err(AllocationError::AllocatorNotAvailable(
402                        "Arena allocator not enabled".to_string(),
403                    ))
404                }
405            }
406        };
407
408        let allocation_time = start_time.elapsed().as_nanos() as f64;
409
410        match &result {
411            Ok(_) => {
412                self.stats.bytes_allocated += size as u64;
413                self.stats.current_memory_usage += size;
414                if self.stats.current_memory_usage > self.stats.peak_memory_usage {
415                    self.stats.peak_memory_usage = self.stats.current_memory_usage;
416                }
417
418                // Update strategy-specific stats
419                match &allocator_type {
420                    AllocatorType::Strategy(strategy) => {
421                        *self
422                            .stats
423                            .strategy_allocations
424                            .entry(strategy.clone())
425                            .or_insert(0) += 1;
426                    }
427                    AllocatorType::Buddy => self.stats.buddy_allocations += 1,
428                    AllocatorType::Slab => self.stats.slab_allocations += 1,
429                    AllocatorType::Arena => self.stats.arena_allocations += 1,
430                }
431
432                // Update performance metrics
433                let metrics = PerformanceMetrics {
434                    average_latency_ns: allocation_time,
435                    success_rate: 1.0,
436                    fragmentation_ratio: 0.0, // Would need to calculate from allocator
437                    cache_hit_rate: 0.0,      // Would need to get from allocator
438                    memory_efficiency: 1.0,   // Would need to calculate
439                };
440                self.routing_table
441                    .update_performance(allocator_type, metrics);
442            }
443            Err(_) => {
444                // Update failure metrics
445                let metrics = PerformanceMetrics {
446                    average_latency_ns: allocation_time,
447                    success_rate: 0.0,
448                    ..Default::default()
449                };
450                self.routing_table
451                    .update_performance(allocator_type, metrics);
452            }
453        }
454
455        // Update average allocation time
456        let total_time = self.stats.average_allocation_time_ns
457            * (self.stats.total_allocations - 1) as f64
458            + allocation_time;
459        self.stats.average_allocation_time_ns = total_time / self.stats.total_allocations as f64;
460
461        result.map(|ptr| unsafe { NonNull::new_unchecked(ptr) })
462    }
463
464    /// Deallocate memory
465    pub fn deallocate(&mut self, ptr: NonNull<u8>, size: usize) -> Result<(), AllocationError> {
466        self.stats.total_deallocations += 1;
467        self.stats.bytes_deallocated += size as u64;
468        self.stats.current_memory_usage = self.stats.current_memory_usage.saturating_sub(size);
469
470        // Try each allocator to find which one owns this pointer
471        if let Some(ref mut buddy) = self.buddy_allocator {
472            if let Ok(()) = buddy.deallocate(ptr.as_ptr()) {
473                return Ok(());
474            }
475        }
476
477        if let Some(ref mut slab) = self.slab_allocator {
478            if let Ok(()) = slab.deallocate(ptr, size) {
479                return Ok(());
480            }
481        }
482
483        if let Some(ref mut arena) = self.arena_allocator {
484            if arena.contains_pointer(ptr) {
485                // Arena allocator typically doesn't support individual deallocation
486                return Ok(());
487            }
488        }
489
490        Err(AllocationError::InvalidPointer(
491            "Pointer not found in any allocator".to_string(),
492        ))
493    }
494
495    /// Free memory (alias for deallocate)
496    pub fn free(
497        &mut self,
498        ptr: *mut std::ffi::c_void,
499        _allocator_type: AllocatorType,
500    ) -> Result<(), AllocationError> {
501        // Convert to NonNull<u8> and call deallocate
502        let ptr_u8 = NonNull::new(ptr as *mut u8)
503            .ok_or_else(|| AllocationError::InvalidPointer("Null pointer".to_string()))?;
504        // We don't have the size here, so we'll just try to deallocate with a dummy size
505        // This is not ideal, but matches the interface expected by the caller
506        self.deallocate(ptr_u8, 0)
507    }
508
509    /// Allocate a fresh block for a reallocation request.
510    ///
511    /// Deliberately does *not* free `_ptr` (its parameter is unused by
512    /// design, not by oversight): the sole caller,
513    /// [`crate::memory::GpuMemorySystem::reallocate`], already owns the
514    /// complete, correct realloc contract one layer up -- it tracks the old
515    /// allocation's real size (which this layer does not), copies the live
516    /// data with it via `copy_nonoverlapping`, and only then frees the old
517    /// pointer. Freeing `_ptr` here as well would race that caller: it
518    /// still reads from `_ptr` after this call returns whenever the new
519    /// address differs (the overwhelmingly common case, since this always
520    /// allocates fresh rather than truly growing in place), which would
521    /// make that read a use-after-free and the caller's own free a
522    /// double-free.
523    pub fn reallocate(
524        &mut self,
525        _ptr: *mut std::ffi::c_void,
526        new_size: usize,
527        allocator_type: AllocatorType,
528    ) -> Result<*mut std::ffi::c_void, AllocationError> {
529        // For simplicity, implement as a fresh allocation; the caller
530        // (see the doc comment above) handles copying and freeing the old
531        // block. In a production system, this should instead be optimized
532        // for true in-place reallocation using `_ptr`.
533        let new_ptr = self.allocate(new_size, allocator_type, None)?;
534        Ok(new_ptr.as_ptr() as *mut std::ffi::c_void)
535    }
536
537    /// Get unified statistics
538    pub fn get_stats(&self) -> &UnifiedStats {
539        &self.stats
540    }
541
542    /// Get the active configuration (thresholds, enabled sub-allocators,
543    /// default strategy).
544    ///
545    /// `allocate` currently always uses the type its caller passes rather
546    /// than deriving one from `buddy_threshold`/`slab_threshold`/
547    /// `arena_threshold`/`enable_auto_routing` -- centralizing that
548    /// decision here would change
549    /// `GpuMemorySystem::choose_allocator`'s tested
550    /// routing boundaries (its hardcoded 1 KiB / 1 MiB cutovers do not
551    /// line up with this config's threshold values), which is a
552    /// deliberate behavior decision left to that caller rather than one
553    /// this lint pass makes unilaterally. Exposed so callers can inspect
554    /// (and eventually route against) the real configuration instead of
555    /// duplicating their own hardcoded thresholds.
556    pub fn get_config(&self) -> &UnifiedConfig {
557        &self.config
558    }
559
560    /// Get detailed allocator information
561    pub fn get_detailed_info(&self) -> DetailedAllocatorInfo {
562        let mut info = DetailedAllocatorInfo {
563            strategy_info: Some(self.strategy_manager.get_stats().clone()),
564            buddy_info: None,
565            slab_info: None,
566            arena_info: None,
567            unified_stats: self.stats.clone(),
568        };
569
570        if let Some(ref buddy) = self.buddy_allocator {
571            info.buddy_info = Some(buddy.get_stats().clone());
572        }
573
574        if let Some(ref slab) = self.slab_allocator {
575            info.slab_info = Some(slab.get_stats());
576        }
577
578        if let Some(ref arena) = self.arena_allocator {
579            info.arena_info = Some(arena.get_stats().clone());
580        }
581
582        info
583    }
584
585    /// Reset specific allocator
586    pub fn reset_allocator(
587        &mut self,
588        allocator_type: AllocatorType,
589    ) -> Result<(), AllocationError> {
590        match allocator_type {
591            AllocatorType::Strategy(_) => {
592                self.strategy_manager.clear_history();
593            }
594            AllocatorType::Buddy => {
595                if let Some(ref mut buddy) = self.buddy_allocator {
596                    buddy.reset();
597                } else {
598                    return Err(AllocationError::AllocatorNotAvailable(
599                        "Buddy allocator not enabled".to_string(),
600                    ));
601                }
602            }
603            AllocatorType::Slab => {
604                return Err(AllocationError::UnsupportedOperation(
605                    "Slab allocator reset not supported".to_string(),
606                ));
607            }
608            AllocatorType::Arena => {
609                if let Some(ref mut arena) = self.arena_allocator {
610                    arena.reset();
611                } else {
612                    return Err(AllocationError::AllocatorNotAvailable(
613                        "Arena allocator not enabled".to_string(),
614                    ));
615                }
616            }
617        }
618        Ok(())
619    }
620
621    /// Force garbage collection on applicable allocators
622    pub fn garbage_collect(&mut self) -> GarbageCollectionResult {
623        let mut result = GarbageCollectionResult::default();
624
625        if let Some(ref mut slab) = self.slab_allocator {
626            result.slab_reclaimed = slab.reclaim_memory();
627        }
628
629        if let Some(ref mut buddy) = self.buddy_allocator {
630            result.buddy_defragmented = buddy.defragment();
631        }
632
633        result
634    }
635
636    /// Optimize allocation strategies based on performance data
637    pub fn optimize_strategies(&mut self) -> Result<(), AllocationError> {
638        // Update internal routing table based on performance metrics
639        let current_stats = self.get_stats();
640
641        // Analyze allocation patterns and update routing decisions
642        // Note: Routing optimization would be implemented here based on performance history
643        if current_stats.buddy_allocations > current_stats.slab_allocations {
644            // Prefer buddy allocator for current workload - implementation would update routing config
645        } else {
646            // Prefer slab allocator for current workload - implementation would update routing config
647        }
648
649        // Reset counters for next optimization cycle
650        self.stats.buddy_allocations = 0;
651        self.stats.slab_allocations = 0;
652        self.stats.arena_allocations = 0;
653        self.stats.strategy_allocations.clear();
654
655        Ok(())
656    }
657}
658
659// Safety: UnifiedAllocator contains multiple allocators managing GPU memory pointers.
660// While the contained allocators use NonNull/raw pointers that aren't Send/Sync by default,
661// it's safe to share UnifiedAllocator across threads when protected by Arc<Mutex<>> because:
662// 1. All pointers point to GPU memory managed by the GPU driver
663// 2. The Mutex provides exclusive access for all mutable operations
664// 3. No thread-local state is maintained
665unsafe impl Send for UnifiedAllocator {}
666unsafe impl Sync for UnifiedAllocator {}
667
668/// Detailed information about all allocators
669#[derive(Debug, Clone)]
670pub struct DetailedAllocatorInfo {
671    pub strategy_info: Option<AllocationStats>,
672    pub buddy_info: Option<BuddyStats>,
673    pub slab_info: Option<SlabAllocatorStats>,
674    pub arena_info: Option<ArenaStats>,
675    pub unified_stats: UnifiedStats,
676}
677
678/// Result of garbage collection operations
679#[derive(Debug, Clone, Default)]
680pub struct GarbageCollectionResult {
681    pub slab_reclaimed: usize,
682    pub buddy_defragmented: usize,
683    pub arena_reset: bool,
684    pub total_bytes_freed: usize,
685}
686
687/// Unified allocation errors
688#[derive(Debug, Clone)]
689pub enum AllocationError {
690    OutOfMemory(String),
691    InvalidPointer(String),
692    AllocatorNotAvailable(String),
693    UnsupportedOperation(String),
694    BuddyError(BuddyError),
695    SlabError(SlabError),
696    ArenaError(ArenaError),
697}
698
699impl std::fmt::Display for AllocationError {
700    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
701        match self {
702            AllocationError::OutOfMemory(msg) => write!(f, "Out of memory: {}", msg),
703            AllocationError::InvalidPointer(msg) => write!(f, "Invalid pointer: {}", msg),
704            AllocationError::AllocatorNotAvailable(msg) => {
705                write!(f, "Allocator not available: {}", msg)
706            }
707            AllocationError::UnsupportedOperation(msg) => {
708                write!(f, "Unsupported operation: {}", msg)
709            }
710            AllocationError::BuddyError(e) => write!(f, "Buddy allocator error: {}", e),
711            AllocationError::SlabError(e) => write!(f, "Slab allocator error: {}", e),
712            AllocationError::ArenaError(e) => write!(f, "Arena allocator error: {}", e),
713        }
714    }
715}
716
717impl std::error::Error for AllocationError {}
718
719impl From<BuddyError> for AllocationError {
720    fn from(error: BuddyError) -> Self {
721        AllocationError::BuddyError(error)
722    }
723}
724
725impl From<SlabError> for AllocationError {
726    fn from(error: SlabError) -> Self {
727        AllocationError::SlabError(error)
728    }
729}
730
731impl From<ArenaError> for AllocationError {
732    fn from(error: ArenaError) -> Self {
733        AllocationError::ArenaError(error)
734    }
735}
736
737/// Thread-safe unified allocator wrapper
738pub struct ThreadSafeUnifiedAllocator {
739    allocator: Arc<Mutex<UnifiedAllocator>>,
740}
741
742impl ThreadSafeUnifiedAllocator {
743    pub fn new(
744        base_ptr: NonNull<u8>,
745        total_size: usize,
746        config: UnifiedConfig,
747    ) -> Result<Self, AllocationError> {
748        let allocator = UnifiedAllocator::new(base_ptr, total_size, config)?;
749        Ok(Self {
750            allocator: Arc::new(Mutex::new(allocator)),
751        })
752    }
753
754    pub fn allocate(&self, size: usize) -> Result<NonNull<u8>, AllocationError> {
755        let mut allocator = self.allocator.lock().unwrap_or_else(|e| e.into_inner());
756        allocator.allocate(
757            size,
758            AllocatorType::Strategy(strategies::AllocationStrategy::FirstFit),
759            None,
760        )
761    }
762
763    pub fn deallocate(&self, ptr: NonNull<u8>, size: usize) -> Result<(), AllocationError> {
764        let mut allocator = self.allocator.lock().unwrap_or_else(|e| e.into_inner());
765        allocator.deallocate(ptr, size)
766    }
767
768    pub fn get_stats(&self) -> UnifiedStats {
769        let allocator = self.allocator.lock().unwrap_or_else(|e| e.into_inner());
770        allocator.get_stats().clone()
771    }
772
773    pub fn garbage_collect(&self) -> GarbageCollectionResult {
774        let mut allocator = self.allocator.lock().unwrap_or_else(|e| e.into_inner());
775        allocator.garbage_collect()
776    }
777}
778
779#[cfg(test)]
780mod tests {
781    use super::*;
782
783    #[test]
784    fn test_unified_allocator_creation() {
785        let size = 1024 * 1024; // 1MB
786        let memory = vec![0u8; size];
787        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
788
789        let config = UnifiedConfig::default();
790        let allocator = UnifiedAllocator::new(ptr, size, config);
791        assert!(allocator.is_ok());
792    }
793
794    #[test]
795    fn test_get_config_reflects_construction_config() {
796        let size = 1024 * 1024;
797        let memory = vec![0u8; size];
798        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
799
800        let config = UnifiedConfig {
801            buddy_threshold: 2048,
802            ..UnifiedConfig::default()
803        };
804        let allocator = UnifiedAllocator::new(ptr, size, config).expect("unwrap failed");
805
806        assert_eq!(allocator.get_config().buddy_threshold, 2048);
807    }
808
809    #[test]
810    fn test_unified_allocation() {
811        let size = 1024 * 1024;
812        let memory = vec![0u8; size];
813        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
814
815        let config = UnifiedConfig::default();
816        let mut allocator = UnifiedAllocator::new(ptr, size, config).expect("unwrap failed");
817
818        // Test different sizes to trigger different allocators
819        let small_alloc = allocator.allocate(100, AllocatorType::Slab, None); // Should use slab
820        assert!(small_alloc.is_ok());
821
822        let medium_alloc = allocator.allocate(2048, AllocatorType::Buddy, None); // Should use buddy
823        assert!(
824            medium_alloc.is_ok(),
825            "Medium allocation failed: {:?}",
826            medium_alloc.err()
827        );
828
829        let large_alloc = allocator.allocate(128 * 1024, AllocatorType::Arena, None); // Should use arena
830        assert!(large_alloc.is_ok());
831
832        let stats = allocator.get_stats();
833        assert_eq!(stats.total_allocations, 3);
834    }
835
836    #[test]
837    fn test_allocation_routing() {
838        let config = RouterConfig::default();
839        let mut router = AllocationRouter::new(config);
840
841        let small_route = router.route_allocation(100, None);
842        assert_eq!(small_route, AllocatorType::Slab);
843
844        let medium_route = router.route_allocation(2048, None);
845        assert_eq!(
846            medium_route,
847            AllocatorType::Strategy(AllocationStrategy::BestFit)
848        );
849
850        let large_route = router.route_allocation(128 * 1024, None);
851        assert_eq!(large_route, AllocatorType::Arena);
852    }
853
854    #[test]
855    fn test_thread_safe_unified_allocator() {
856        let size = 1024 * 1024;
857        let memory = vec![0u8; size];
858        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
859
860        let config = UnifiedConfig::default();
861        let allocator = ThreadSafeUnifiedAllocator::new(ptr, size, config).expect("unwrap failed");
862
863        let alloc_result = allocator.allocate(1024);
864        assert!(
865            alloc_result.is_ok(),
866            "Allocation failed: {:?}",
867            alloc_result.err()
868        );
869
870        let stats = allocator.get_stats();
871        assert!(stats.total_allocations > 0);
872    }
873}