1use std::sync::{
7 Arc,
8 atomic::{AtomicU64, Ordering},
9};
10
11use anyhow::{Context, Result};
12use serde::{Deserialize, Serialize};
13use crate::dev_log;
14#[allow(unused_imports)]
15use wasmtime::{Memory, MemoryType};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct MemoryLimits {
20 pub max_memory_mb:u64,
22 pub initial_memory_mb:u64,
24 pub max_table_size:u32,
26 pub max_memories:usize,
28 pub max_tables:usize,
30 pub max_instances:usize,
32}
33
34impl Default for MemoryLimits {
35 fn default() -> Self {
36 Self {
37 max_memory_mb:512,
38 initial_memory_mb:64,
39 max_table_size:1024,
40 max_memories:10,
41 max_tables:10,
42 max_instances:100,
43 }
44 }
45}
46
47impl MemoryLimits {
48 pub fn new(max_memory_mb:u64, initial_memory_mb:u64, max_instances:usize) -> Self {
50 Self { max_memory_mb, initial_memory_mb, max_instances, ..Default::default() }
51 }
52
53 pub fn max_memory_bytes(&self) -> u64 { self.max_memory_mb * 1024 * 1024 }
55
56 pub fn initial_memory_bytes(&self) -> u64 { self.initial_memory_mb * 1024 * 1024 }
58
59 pub fn validate_request(&self, requested_bytes:u64, current_usage:u64) -> Result<()> {
61 if current_usage + requested_bytes > self.max_memory_bytes() {
62 return Err(anyhow::anyhow!(
63 "Memory request exceeds limit: {} + {} > {} bytes",
64 current_usage,
65 requested_bytes,
66 self.max_memory_bytes()
67 ));
68 }
69 Ok(())
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct MemoryAllocation {
76 pub id:String,
78 pub instance_id:String,
80 pub memory_type:String,
82 pub size_bytes:u64,
84 pub max_size_bytes:u64,
86 pub allocated_at:u64,
88 pub is_shared:bool,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct MemoryStats {
95 pub total_allocated:u64,
97 pub total_allocated_mb:f64,
99 pub allocation_count:usize,
101 pub deallocation_count:usize,
103 pub peak_memory_bytes:u64,
105 pub peak_memory_mb:f64,
107}
108
109impl Default for MemoryStats {
110 fn default() -> Self {
111 Self {
112 total_allocated:0,
113 total_allocated_mb:0.0,
114 allocation_count:0,
115 deallocation_count:0,
116 peak_memory_bytes:0,
117 peak_memory_mb:0.0,
118 }
119 }
120}
121
122impl MemoryStats {
123 pub fn record_allocation(&mut self, size_bytes:u64) {
125 self.total_allocated += size_bytes;
126 self.allocation_count += 1;
127 if self.total_allocated > self.peak_memory_bytes {
128 self.peak_memory_bytes = self.total_allocated;
129 }
130 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
131 self.peak_memory_mb = self.peak_memory_bytes as f64 / (1024.0 * 1024.0);
132 }
133
134 pub fn record_deallocation(&mut self, size_bytes:u64) {
136 self.total_allocated = self.total_allocated.saturating_sub(size_bytes);
137 self.deallocation_count += 1;
138 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
139 }
140}
141
142#[derive(Debug)]
144pub struct MemoryManagerImpl {
145 limits:MemoryLimits,
146 allocations:Vec<MemoryAllocation>,
147 stats:Arc<MemoryStats>,
148 peak_usage:Arc<AtomicU64>,
149}
150
151impl MemoryManagerImpl {
152 pub fn new(limits:MemoryLimits) -> Self {
154 Self {
155 limits,
156 allocations:Vec::new(),
157 stats:Arc::new(MemoryStats::default()),
158 peak_usage:Arc::new(AtomicU64::new(0)),
159 }
160 }
161
162 pub fn limits(&self) -> &MemoryLimits { &self.limits }
164
165 pub fn stats(&self) -> &MemoryStats { &self.stats }
167
168 pub fn peak_usage_bytes(&self) -> u64 { self.peak_usage.load(Ordering::Relaxed) }
170
171 pub fn peak_usage_mb(&self) -> f64 { self.peak_usage.load(Ordering::Relaxed) as f64 / (1024.0 * 1024.0) }
173
174 pub fn current_usage_bytes(&self) -> u64 { self.allocations.iter().map(|a| a.size_bytes).sum() }
176
177 pub fn current_usage_mb(&self) -> f64 { self.current_usage_bytes() as f64 / (1024.0 * 1024.0) }
179
180 pub fn can_allocate(&self, requested_bytes:u64) -> bool {
182 let current = self.current_usage_bytes();
183 current + requested_bytes <= self.limits.max_memory_bytes()
184 }
185
186 pub fn allocate_memory(&mut self, instance_id:&str, memory_type:&str, requested_bytes:u64) -> Result<u64> {
188 dev_log!("wasm", "Allocating {} bytes for instance {} (type: {})", requested_bytes, instance_id, memory_type);
189
190 let current_usage = self.current_usage_bytes();
191
192 self.limits
194 .validate_request(requested_bytes, current_usage)
195 .context("Memory allocation validation failed")?;
196
197 if self.allocations.len() >= self.limits.max_memories {
199 return Err(anyhow::anyhow!(
200 "Maximum number of memory allocations reached: {}",
201 self.limits.max_memories
202 ));
203 }
204
205 let allocation = MemoryAllocation {
207 id:format!("alloc-{}", uuid::Uuid::new_v4()),
208 instance_id:instance_id.to_string(),
209 memory_type:memory_type.to_string(),
210 size_bytes:requested_bytes,
211 max_size_bytes:self.limits.max_memory_bytes() - current_usage,
212 allocated_at:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs(),
213 is_shared:false,
214 };
215
216 self.allocations.push(allocation);
217
218 Arc::make_mut(&mut self.stats).record_allocation(requested_bytes);
220
221 let new_peak = self.current_usage_bytes();
223 let current_peak = self.peak_usage.load(Ordering::Relaxed);
224 if new_peak > current_peak {
225 self.peak_usage.store(new_peak, Ordering::Relaxed);
226 }
227
228 dev_log!("wasm", "Memory allocated successfully. Total usage: {} MB", self.current_usage_mb());
229
230 Ok(requested_bytes)
231 }
232
233 pub fn deallocate_memory(&mut self, instance_id:&str, memory_id:&str) -> Result<bool> {
235 dev_log!("wasm", "Deallocating memory {} for instance {}", memory_id, instance_id);
236
237 let pos = self
238 .allocations
239 .iter()
240 .position(|a| a.instance_id == instance_id && a.id == memory_id);
241
242 if let Some(pos) = pos {
243 let allocation = self.allocations.remove(pos);
244
245 Arc::make_mut(&mut self.stats).record_deallocation(allocation.size_bytes);
247
248 dev_log!("wasm", "Memory deallocated successfully. Remaining usage: {} MB", self.current_usage_mb());
249
250 Ok(true)
251 } else {
252 dev_log!("wasm", "warn: memory allocation not found: {} for instance {}", memory_id, instance_id);
253 Ok(false)
254 }
255 }
256
257 pub fn deallocate_all_for_instance(&mut self, instance_id:&str) -> usize {
259 dev_log!("wasm", "Deallocating all memory for instance {}", instance_id);
260
261 let initial_count = self.allocations.len();
262
263 self.allocations.retain(|a| a.instance_id != instance_id);
264
265 let deallocated_count = initial_count - self.allocations.len();
266
267 if deallocated_count > 0 {
268 dev_log!("wasm", "Deallocated {} memory allocations for instance {}", deallocated_count, instance_id);
269 }
270
271 deallocated_count
272 }
273
274 pub fn grow_memory(&mut self, instance_id:&str, memory_id:&str, additional_bytes:u64) -> Result<u64> {
276 dev_log!("wasm", "Growing memory {} for instance {} by {} bytes", memory_id, instance_id, additional_bytes);
277
278 let current_usage = self.current_usage_bytes();
280
281 let allocation = self
282 .allocations
283 .iter_mut()
284 .find(|a| a.instance_id == instance_id && a.id == memory_id)
285 .ok_or_else(|| anyhow::anyhow!("Memory allocation not found"))?;
286
287 self.limits
289 .validate_request(additional_bytes, current_usage)
290 .context("Memory growth validation failed")?;
291
292 allocation.size_bytes += additional_bytes;
293
294 dev_log!("wasm", "Memory grown successfully. New size: {} bytes", allocation.size_bytes);
295
296 Ok(allocation.size_bytes)
297 }
298
299 pub fn get_allocations_for_instance(&self, instance_id:&str) -> Vec<&MemoryAllocation> {
301 self.allocations.iter().filter(|a| a.instance_id == instance_id).collect()
302 }
303
304 pub fn is_exceeded(&self) -> bool { self.current_usage_bytes() > self.limits.max_memory_bytes() }
306
307 pub fn usage_percentage(&self) -> f64 {
309 (self.current_usage_bytes() as f64 / self.limits.max_memory_bytes() as f64) * 100.0
310 }
311
312 pub fn reset(&mut self) {
314 self.allocations.clear();
315 self.stats = Arc::new(MemoryStats::default());
316 self.peak_usage.store(0, Ordering::Relaxed);
317 dev_log!("wasm", "Memory manager reset");
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn test_memory_limits_default() {
327 let limits = MemoryLimits::default();
328 assert_eq!(limits.max_memory_mb, 512);
329 assert_eq!(limits.initial_memory_mb, 64);
330 }
331
332 #[test]
333 fn test_memory_limits_custom() {
334 let limits = MemoryLimits::new(1024, 128, 50);
335 assert_eq!(limits.max_memory_mb, 1024);
336 assert_eq!(limits.initial_memory_mb, 128);
337 assert_eq!(limits.max_instances, 50);
338 }
339
340 #[test]
341 fn test_memory_limits_validation() {
342 let limits = MemoryLimits::new(100, 10, 10);
343
344 assert!(limits.validate_request(50, 0).is_ok());
346
347 assert!(limits.validate_request(150, 0).is_err());
349 assert!(limits.validate_request(50, 60).is_err());
350 }
351
352 #[test]
353 fn test_memory_manager_creation() {
354 let limits = MemoryLimits::default();
355 let manager = MemoryManagerImpl::new(limits);
356 assert_eq!(manager.current_usage_bytes(), 0);
357 assert_eq!(manager.allocations.len(), 0);
358 }
359
360 #[test]
361 fn test_memory_allocation() {
362 let limits = MemoryLimits::default();
363 let mut manager = MemoryManagerImpl::new(limits);
364
365 let result = manager.allocate_memory("test-instance", "heap", 1024);
366 assert!(result.is_ok());
367 assert_eq!(manager.current_usage_bytes(), 1024);
368 assert_eq!(manager.allocations.len(), 1);
369 }
370
371 #[test]
372 fn test_memory_deallocation() {
373 let limits = MemoryLimits::default();
374 let mut manager = MemoryManagerImpl::new(limits);
375
376 manager.allocate_memory("test-instance", "heap", 1024).unwrap();
377 let allocation = &manager.allocations[0];
378 let memory_id = allocation.id.clone();
379
380 let result = manager.deallocate_memory("test-instance", &memory_id);
381 assert!(result.is_ok());
382 assert_eq!(manager.current_usage_bytes(), 0);
383 assert_eq!(manager.allocations.len(), 0);
384 }
385
386 #[test]
387 fn test_memory_stats() {
388 let mut stats = MemoryStats::default();
389 stats.record_allocation(1024);
390 assert_eq!(stats.allocation_count, 1);
391 assert_eq!(stats.total_allocated, 1024);
392
393 stats.record_deallocation(512);
394 assert_eq!(stats.deallocation_count, 1);
395 assert_eq!(stats.total_allocated, 512);
396 }
397
398 #[test]
399 fn test_memory_usage_percentage() {
400 let limits = MemoryLimits::new(1000, 0, 0);
401 let mut manager = MemoryManagerImpl::new(limits);
402
403 manager.allocate_memory("test", "heap", 500).unwrap();
404 assert_eq!(manager.usage_percentage(), 50.0);
405 }
406}