-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Problem
The current memory system violates every principle of elegant design:
- ❌ Single Responsibility: CapabilityAwareProvider does capability checking AND memory management
- ❌ Composition: Complex factory hierarchies instead of simple composition
- ❌ No Circular Dependencies: Provider creation can trigger module creation
- ❌ Minimal Global State: Heavy reliance on global capability context
- ❌ Type Safety: Type mismatches between different provider types
- ❌ Clear Error Handling: "Generated error" masks real issues
- ❌ Performance: Multiple layers of indirection and global lookups
Current Architecture (5+ layers)
NoStdProvider → CapabilityAwareProvider → RuntimeProvider → MemoryCapabilityContext → Factory patterns
Each layer adds complexity, potential circular dependencies, and failure points.
Proposed Elegant Architecture (3 components)
Component 1: MemoryBlock (replaces NoStdProvider)
struct MemoryBlock<const N: usize> {
data: [u8; N],
used: AtomicUsize,
}
impl<const N: usize> MemoryBlock<N> {
pub fn new() -> Self { /* simple constructor */ }
pub fn allocate(&self, size: usize) -> Option<&mut [u8]> { /* atomic allocation */ }
pub fn deallocate(&self, ptr: *mut u8, size: usize) { /* atomic deallocation */ }
}
- Single responsibility: Just raw memory management
- No capability logic: Pure memory operations
- No global dependencies: Self-contained
Component 2: BudgetTracker (replaces CapabilityContext)
struct BudgetTracker {
budgets: [AtomicUsize; NUM_CRATES], // Static array, no heap allocation
}
impl BudgetTracker {
pub fn allocate(&self, crate_id: CrateId, size: usize) -> Result<()> {
let budget = &self.budgets[crate_id as usize];
let remaining = budget.fetch_sub(size, Ordering::AcqRel);
if remaining < size {
budget.fetch_add(size, Ordering::AcqRel); // Return budget
return Err(Error::budget_exceeded());
}
Ok(())
}
pub fn deallocate(&self, crate_id: CrateId, size: usize) {
self.budgets[crate_id as usize].fetch_add(size, Ordering::AcqRel);
}
}
- Single responsibility: Only budget tracking
- Thread-safe: Atomic operations
- No memory management: Pure accounting
Component 3: ManagedMemory (replaces CapabilityAwareProvider)
struct ManagedMemory<const N: usize> {
block: MemoryBlock<N>,
budget_tracker: &'static BudgetTracker,
crate_id: CrateId,
}
impl<const N: usize> ManagedMemory<N> {
pub fn new(crate_id: CrateId) -> Result<Self> {
GLOBAL_BUDGET_TRACKER.allocate(crate_id, N)?;
Ok(Self {
block: MemoryBlock::new(),
budget_tracker: &GLOBAL_BUDGET_TRACKER,
crate_id,
})
}
}
impl<const N: usize> Drop for ManagedMemory<N> {
fn drop(&mut self) {
self.budget_tracker.deallocate(self.crate_id, N);
}
}
- Composition: Combines MemoryBlock + BudgetTracker
- RAII: Automatic budget cleanup
- Simple interface: Clean abstractions
Single Allocation API (replaces all factory complexity)
pub fn allocate_memory<const N: usize>(crate_id: CrateId) -> Result<ManagedMemory<N>> {
ManagedMemory::<N>::new(crate_id)
}
// Usage:
let provider = allocate_memory::<32768>(CrateId::Runtime)?;
let mut vec = BoundedVec::new(provider)?;
Expected Benefits
- ✅ 70% reduction in complexity: 3 simple components vs 5+ complex layers
- ✅ No circular dependencies: Clear, linear dependency flow
- ✅ Consistent performance: No global context lookups or factory overhead
- ✅ Better error handling: Each component has clear failure modes
- ✅ Easier maintenance: Simple, understandable code
- ✅ Better testing: Each component can be tested in isolation
- ✅ Cross-platform consistency: No platform-specific workarounds needed
Migration Strategy
Phase 1: Implement new components alongside existing system
- Add MemoryBlock, BudgetTracker, ManagedMemory types
- Keep old API as wrapper around new system
- Add comprehensive tests for new components
Phase 2: Migrate core modules
- Update Module, BoundedVec, BoundedMap to use new system
- Maintain API compatibility where possible
- Performance and correctness testing
Phase 3: Remove legacy system
- Remove old provider types and factory patterns
- Clean up unused code and dependencies
- Update documentation
Files Affected
New files:
wrt-foundation/src/memory/memory_block.rs
wrt-foundation/src/memory/budget_tracker.rs
wrt-foundation/src/memory/managed_memory.rs
wrt-foundation/src/memory/mod.rs
Modified files:
wrt-foundation/src/lib.rs
(new public API)wrt-foundation/src/bounded.rs
(use new providers)wrt-runtime/src/module.rs
(simplified initialization)- All files using
safe_managed_alloc!
macro
Priority
MEDIUM - Important for long-term maintainability, but not blocking current functionality
Estimated Time
1-2 weeks (major architectural change, needs careful migration and testing)
Dependencies
- Should be done after issues CRITICAL: Fix circular dependency in memory system macros #114 and SHORT-TERM: Add emergency fallback for Module initialization #115 are complete
- Needs design review and approval before implementation
Success Criteria
- 70%+ reduction in memory system code complexity
- No performance regression in allocation/deallocation
- All existing functionality preserved
- Cross-platform compatibility maintained
- Comprehensive test coverage for new architecture
- Documentation updated to reflect new design
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request