1use std::sync::{
7 Arc,
8 atomic::{AtomicU64, Ordering},
9};
10
11use anyhow::{Context, Result};
12use serde::{Deserialize, Serialize};
13use wasmtime::{Memory, MemoryType};
14
15use crate::dev_log;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct MemoryLimits {
20 pub max_memory_mb:u64,
22
23 pub initial_memory_mb:u64,
25
26 pub max_table_size:u32,
28
29 pub max_memories:usize,
31
32 pub max_tables:usize,
34
35 pub max_instances:usize,
37}
38
39impl Default for MemoryLimits {
40 fn default() -> Self {
41 Self {
42 max_memory_mb:512,
43
44 initial_memory_mb:64,
45
46 max_table_size:1024,
47
48 max_memories:10,
49
50 max_tables:10,
51
52 max_instances:100,
53 }
54 }
55}
56
57impl MemoryLimits {
58 pub fn new(max_memory_mb:u64, initial_memory_mb:u64, max_instances:usize) -> Self {
60 Self { max_memory_mb, initial_memory_mb, max_instances, ..Default::default() }
61 }
62
63 pub fn max_memory_bytes(&self) -> u64 { self.max_memory_mb * 1024 * 1024 }
65
66 pub fn initial_memory_bytes(&self) -> u64 { self.initial_memory_mb * 1024 * 1024 }
68
69 pub fn validate_request(&self, requested_bytes:u64, current_usage:u64) -> Result<()> {
71 if current_usage + requested_bytes > self.max_memory_bytes() {
72 return Err(anyhow::anyhow!(
73 "Memory request exceeds limit: {} + {} > {} bytes",
74 current_usage,
75 requested_bytes,
76 self.max_memory_bytes()
77 ));
78 }
79
80 Ok(())
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct MemoryAllocation {
87 pub id:String,
89
90 pub instance_id:String,
92
93 pub memory_type:String,
95
96 pub size_bytes:u64,
98
99 pub max_size_bytes:u64,
101
102 pub allocated_at:u64,
104
105 pub is_shared:bool,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct MemoryStats {
112 pub total_allocated:u64,
114
115 pub total_allocated_mb:f64,
117
118 pub allocation_count:usize,
120
121 pub deallocation_count:usize,
123
124 pub peak_memory_bytes:u64,
126
127 pub peak_memory_mb:f64,
129}
130
131impl Default for MemoryStats {
132 fn default() -> Self {
133 Self {
134 total_allocated:0,
135
136 total_allocated_mb:0.0,
137
138 allocation_count:0,
139
140 deallocation_count:0,
141
142 peak_memory_bytes:0,
143
144 peak_memory_mb:0.0,
145 }
146 }
147}
148
149impl MemoryStats {
150 pub fn record_allocation(&mut self, size_bytes:u64) {
152 self.total_allocated += size_bytes;
153
154 self.allocation_count += 1;
155
156 if self.total_allocated > self.peak_memory_bytes {
157 self.peak_memory_bytes = self.total_allocated;
158 }
159
160 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
161
162 self.peak_memory_mb = self.peak_memory_bytes as f64 / (1024.0 * 1024.0);
163 }
164
165 pub fn record_deallocation(&mut self, size_bytes:u64) {
167 self.total_allocated = self.total_allocated.saturating_sub(size_bytes);
168
169 self.deallocation_count += 1;
170
171 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
172 }
173}
174
175#[derive(Debug)]
177pub struct MemoryManagerImpl {
178 limits:MemoryLimits,
179
180 allocations:Vec<MemoryAllocation>,
181
182 stats:Arc<MemoryStats>,
183
184 peak_usage:Arc<AtomicU64>,
185}
186
187impl MemoryManagerImpl {
188 pub fn new(limits:MemoryLimits) -> Self {
190 Self {
191 limits,
192
193 allocations:Vec::new(),
194
195 stats:Arc::new(MemoryStats::default()),
196
197 peak_usage:Arc::new(AtomicU64::new(0)),
198 }
199 }
200
201 pub fn limits(&self) -> &MemoryLimits { &self.limits }
203
204 pub fn stats(&self) -> &MemoryStats { &self.stats }
206
207 pub fn peak_usage_bytes(&self) -> u64 { self.peak_usage.load(Ordering::Relaxed) }
209
210 pub fn peak_usage_mb(&self) -> f64 { self.peak_usage.load(Ordering::Relaxed) as f64 / (1024.0 * 1024.0) }
212
213 pub fn current_usage_bytes(&self) -> u64 { self.allocations.iter().map(|a| a.size_bytes).sum() }
215
216 pub fn current_usage_mb(&self) -> f64 { self.current_usage_bytes() as f64 / (1024.0 * 1024.0) }
218
219 pub fn can_allocate(&self, requested_bytes:u64) -> bool {
221 let current = self.current_usage_bytes();
222
223 current + requested_bytes <= self.limits.max_memory_bytes()
224 }
225
226 pub fn allocate_memory(&mut self, instance_id:&str, memory_type:&str, requested_bytes:u64) -> Result<u64> {
228 dev_log!(
229 "wasm",
230 "Allocating {} bytes for instance {} (type: {})",
231 requested_bytes,
232 instance_id,
233 memory_type
234 );
235
236 let current_usage = self.current_usage_bytes();
237
238 self.limits
240 .validate_request(requested_bytes, current_usage)
241 .context("Memory allocation validation failed")?;
242
243 if self.allocations.len() >= self.limits.max_memories {
245 return Err(anyhow::anyhow!(
246 "Maximum number of memory allocations reached: {}",
247 self.limits.max_memories
248 ));
249 }
250
251 let allocation = MemoryAllocation {
253 id:format!("alloc-{}", uuid::Uuid::new_v4()),
254
255 instance_id:instance_id.to_string(),
256
257 memory_type:memory_type.to_string(),
258
259 size_bytes:requested_bytes,
260
261 max_size_bytes:self.limits.max_memory_bytes() - current_usage,
262
263 allocated_at:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs(),
264
265 is_shared:false,
266 };
267
268 self.allocations.push(allocation);
269
270 Arc::make_mut(&mut self.stats).record_allocation(requested_bytes);
272
273 let new_peak = self.current_usage_bytes();
275
276 let current_peak = self.peak_usage.load(Ordering::Relaxed);
277
278 if new_peak > current_peak {
279 self.peak_usage.store(new_peak, Ordering::Relaxed);
280 }
281
282 dev_log!(
283 "wasm",
284 "Memory allocated successfully. Total usage: {} MB",
285 self.current_usage_mb()
286 );
287
288 Ok(requested_bytes)
289 }
290
291 pub fn deallocate_memory(&mut self, instance_id:&str, memory_id:&str) -> Result<bool> {
293 dev_log!("wasm", "Deallocating memory {} for instance {}", memory_id, instance_id);
294
295 let pos = self
296 .allocations
297 .iter()
298 .position(|a| a.instance_id == instance_id && a.id == memory_id);
299
300 if let Some(pos) = pos {
301 let allocation = self.allocations.remove(pos);
302
303 Arc::make_mut(&mut self.stats).record_deallocation(allocation.size_bytes);
305
306 dev_log!(
307 "wasm",
308 "Memory deallocated successfully. Remaining usage: {} MB",
309 self.current_usage_mb()
310 );
311
312 Ok(true)
313 } else {
314 dev_log!(
315 "wasm",
316 "warn: memory allocation not found: {} for instance {}",
317 memory_id,
318 instance_id
319 );
320
321 Ok(false)
322 }
323 }
324
325 pub fn deallocate_all_for_instance(&mut self, instance_id:&str) -> usize {
327 dev_log!("wasm", "Deallocating all memory for instance {}", instance_id);
328
329 let initial_count = self.allocations.len();
330
331 self.allocations.retain(|a| a.instance_id != instance_id);
332
333 let deallocated_count = initial_count - self.allocations.len();
334
335 if deallocated_count > 0 {
336 dev_log!(
337 "wasm",
338 "Deallocated {} memory allocations for instance {}",
339 deallocated_count,
340 instance_id
341 );
342 }
343
344 deallocated_count
345 }
346
347 pub fn grow_memory(&mut self, instance_id:&str, memory_id:&str, additional_bytes:u64) -> Result<u64> {
349 dev_log!(
350 "wasm",
351 "Growing memory {} for instance {} by {} bytes",
352 memory_id,
353 instance_id,
354 additional_bytes
355 );
356
357 let current_usage = self.current_usage_bytes();
359
360 let allocation = self
361 .allocations
362 .iter_mut()
363 .find(|a| a.instance_id == instance_id && a.id == memory_id)
364 .ok_or_else(|| anyhow::anyhow!("Memory allocation not found"))?;
365
366 self.limits
368 .validate_request(additional_bytes, current_usage)
369 .context("Memory growth validation failed")?;
370
371 allocation.size_bytes += additional_bytes;
372
373 dev_log!("wasm", "Memory grown successfully. New size: {} bytes", allocation.size_bytes);
374
375 Ok(allocation.size_bytes)
376 }
377
378 pub fn get_allocations_for_instance(&self, instance_id:&str) -> Vec<&MemoryAllocation> {
380 self.allocations.iter().filter(|a| a.instance_id == instance_id).collect()
381 }
382
383 pub fn is_exceeded(&self) -> bool { self.current_usage_bytes() > self.limits.max_memory_bytes() }
385
386 pub fn usage_percentage(&self) -> f64 {
388 (self.current_usage_bytes() as f64 / self.limits.max_memory_bytes() as f64) * 100.0
389 }
390
391 pub fn reset(&mut self) {
393 self.allocations.clear();
394
395 self.stats = Arc::new(MemoryStats::default());
396
397 self.peak_usage.store(0, Ordering::Relaxed);
398
399 dev_log!("wasm", "Memory manager reset");
400 }
401}
402
403#[cfg(test)]
404mod tests {
405
406 use super::*;
407
408 #[test]
409 fn test_memory_limits_default() {
410 let limits = MemoryLimits::default();
411
412 assert_eq!(limits.max_memory_mb, 512);
413
414 assert_eq!(limits.initial_memory_mb, 64);
415 }
416
417 #[test]
418 fn test_memory_limits_custom() {
419 let limits = MemoryLimits::new(1024, 128, 50);
420
421 assert_eq!(limits.max_memory_mb, 1024);
422
423 assert_eq!(limits.initial_memory_mb, 128);
424
425 assert_eq!(limits.max_instances, 50);
426 }
427
428 #[test]
429 fn test_memory_limits_validation() {
430 let limits = MemoryLimits::new(100, 10, 10);
431
432 assert!(limits.validate_request(50, 0).is_ok());
434
435 assert!(limits.validate_request(150, 0).is_err());
437
438 assert!(limits.validate_request(50, 60).is_err());
439 }
440
441 #[test]
442 fn test_memory_manager_creation() {
443 let limits = MemoryLimits::default();
444
445 let manager = MemoryManagerImpl::new(limits);
446
447 assert_eq!(manager.current_usage_bytes(), 0);
448
449 assert_eq!(manager.allocations.len(), 0);
450 }
451
452 #[test]
453 fn test_memory_allocation() {
454 let limits = MemoryLimits::default();
455
456 let mut manager = MemoryManagerImpl::new(limits);
457
458 let result = manager.allocate_memory("test-instance", "heap", 1024);
459
460 assert!(result.is_ok());
461
462 assert_eq!(manager.current_usage_bytes(), 1024);
463
464 assert_eq!(manager.allocations.len(), 1);
465 }
466
467 #[test]
468 fn test_memory_deallocation() {
469 let limits = MemoryLimits::default();
470
471 let mut manager = MemoryManagerImpl::new(limits);
472
473 manager.allocate_memory("test-instance", "heap", 1024).unwrap();
474
475 let allocation = &manager.allocations[0];
476
477 let memory_id = allocation.id.clone();
478
479 let result = manager.deallocate_memory("test-instance", &memory_id);
480
481 assert!(result.is_ok());
482
483 assert_eq!(manager.current_usage_bytes(), 0);
484
485 assert_eq!(manager.allocations.len(), 0);
486 }
487
488 #[test]
489 fn test_memory_stats() {
490 let mut stats = MemoryStats::default();
491
492 stats.record_allocation(1024);
493
494 assert_eq!(stats.allocation_count, 1);
495
496 assert_eq!(stats.total_allocated, 1024);
497
498 stats.record_deallocation(512);
499
500 assert_eq!(stats.deallocation_count, 1);
501
502 assert_eq!(stats.total_allocated, 512);
503 }
504
505 #[test]
506 fn test_memory_usage_percentage() {
507 let limits = MemoryLimits::new(1000, 0, 0);
508
509 let mut manager = MemoryManagerImpl::new(limits);
510
511 manager.allocate_memory("test", "heap", 500).unwrap();
512
513 assert_eq!(manager.usage_percentage(), 50.0);
514 }
515}