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