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 pub initial_memory_mb:u64,
25 pub max_table_size:u32,
27 pub max_memories:usize,
29 pub max_tables:usize,
31 pub max_instances:usize,
33}
34
35impl Default for MemoryLimits {
36 fn default() -> Self {
37 Self {
38 max_memory_mb:512,
39 initial_memory_mb:64,
40 max_table_size:1024,
41 max_memories:10,
42 max_tables:10,
43 max_instances:100,
44 }
45 }
46}
47
48impl MemoryLimits {
49 pub fn new(max_memory_mb:u64, initial_memory_mb:u64, max_instances:usize) -> Self {
51 Self { max_memory_mb, initial_memory_mb, max_instances, ..Default::default() }
52 }
53
54 pub fn max_memory_bytes(&self) -> u64 { self.max_memory_mb * 1024 * 1024 }
56
57 pub fn initial_memory_bytes(&self) -> u64 { self.initial_memory_mb * 1024 * 1024 }
59
60 pub fn validate_request(&self, requested_bytes:u64, current_usage:u64) -> Result<()> {
62 if current_usage + requested_bytes > self.max_memory_bytes() {
63 return Err(anyhow::anyhow!(
64 "Memory request exceeds limit: {} + {} > {} bytes",
65 current_usage,
66 requested_bytes,
67 self.max_memory_bytes()
68 ));
69 }
70 Ok(())
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct MemoryAllocation {
77 pub id:String,
79 pub instance_id:String,
81 pub memory_type:String,
83 pub size_bytes:u64,
85 pub max_size_bytes:u64,
87 pub allocated_at:u64,
89 pub is_shared:bool,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct MemoryStats {
96 pub total_allocated:u64,
98 pub total_allocated_mb:f64,
100 pub allocation_count:usize,
102 pub deallocation_count:usize,
104 pub peak_memory_bytes:u64,
106 pub peak_memory_mb:f64,
108}
109
110impl Default for MemoryStats {
111 fn default() -> Self {
112 Self {
113 total_allocated:0,
114 total_allocated_mb:0.0,
115 allocation_count:0,
116 deallocation_count:0,
117 peak_memory_bytes:0,
118 peak_memory_mb:0.0,
119 }
120 }
121}
122
123impl MemoryStats {
124 pub fn record_allocation(&mut self, size_bytes:u64) {
126 self.total_allocated += size_bytes;
127 self.allocation_count += 1;
128 if self.total_allocated > self.peak_memory_bytes {
129 self.peak_memory_bytes = self.total_allocated;
130 }
131 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
132 self.peak_memory_mb = self.peak_memory_bytes as f64 / (1024.0 * 1024.0);
133 }
134
135 pub fn record_deallocation(&mut self, size_bytes:u64) {
137 self.total_allocated = self.total_allocated.saturating_sub(size_bytes);
138 self.deallocation_count += 1;
139 self.total_allocated_mb = self.total_allocated as f64 / (1024.0 * 1024.0);
140 }
141}
142
143#[derive(Debug)]
145pub struct MemoryManagerImpl {
146 limits:MemoryLimits,
147 allocations:Vec<MemoryAllocation>,
148 stats:Arc<MemoryStats>,
149 peak_usage:Arc<AtomicU64>,
150}
151
152impl MemoryManagerImpl {
153 pub fn new(limits:MemoryLimits) -> Self {
155 Self {
156 limits,
157 allocations:Vec::new(),
158 stats:Arc::new(MemoryStats::default()),
159 peak_usage:Arc::new(AtomicU64::new(0)),
160 }
161 }
162
163 pub fn limits(&self) -> &MemoryLimits { &self.limits }
165
166 pub fn stats(&self) -> &MemoryStats { &self.stats }
168
169 pub fn peak_usage_bytes(&self) -> u64 { self.peak_usage.load(Ordering::Relaxed) }
171
172 pub fn peak_usage_mb(&self) -> f64 { self.peak_usage.load(Ordering::Relaxed) as f64 / (1024.0 * 1024.0) }
174
175 pub fn current_usage_bytes(&self) -> u64 { self.allocations.iter().map(|a| a.size_bytes).sum() }
177
178 pub fn current_usage_mb(&self) -> f64 { self.current_usage_bytes() as f64 / (1024.0 * 1024.0) }
180
181 pub fn can_allocate(&self, requested_bytes:u64) -> bool {
183 let current = self.current_usage_bytes();
184 current + requested_bytes <= self.limits.max_memory_bytes()
185 }
186
187 pub fn allocate_memory(&mut self, instance_id:&str, memory_type:&str, requested_bytes:u64) -> Result<u64> {
189 dev_log!(
190 "wasm",
191 "Allocating {} bytes for instance {} (type: {})",
192 requested_bytes,
193 instance_id,
194 memory_type
195 );
196
197 let current_usage = self.current_usage_bytes();
198
199 self.limits
201 .validate_request(requested_bytes, current_usage)
202 .context("Memory allocation validation failed")?;
203
204 if self.allocations.len() >= self.limits.max_memories {
206 return Err(anyhow::anyhow!(
207 "Maximum number of memory allocations reached: {}",
208 self.limits.max_memories
209 ));
210 }
211
212 let allocation = MemoryAllocation {
214 id:format!("alloc-{}", uuid::Uuid::new_v4()),
215 instance_id:instance_id.to_string(),
216 memory_type:memory_type.to_string(),
217 size_bytes:requested_bytes,
218 max_size_bytes:self.limits.max_memory_bytes() - current_usage,
219 allocated_at:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs(),
220 is_shared:false,
221 };
222
223 self.allocations.push(allocation);
224
225 Arc::make_mut(&mut self.stats).record_allocation(requested_bytes);
227
228 let new_peak = self.current_usage_bytes();
230 let current_peak = self.peak_usage.load(Ordering::Relaxed);
231 if new_peak > current_peak {
232 self.peak_usage.store(new_peak, Ordering::Relaxed);
233 }
234
235 dev_log!(
236 "wasm",
237 "Memory allocated successfully. Total usage: {} MB",
238 self.current_usage_mb()
239 );
240
241 Ok(requested_bytes)
242 }
243
244 pub fn deallocate_memory(&mut self, instance_id:&str, memory_id:&str) -> Result<bool> {
246 dev_log!("wasm", "Deallocating memory {} for instance {}", memory_id, instance_id);
247
248 let pos = self
249 .allocations
250 .iter()
251 .position(|a| a.instance_id == instance_id && a.id == memory_id);
252
253 if let Some(pos) = pos {
254 let allocation = self.allocations.remove(pos);
255
256 Arc::make_mut(&mut self.stats).record_deallocation(allocation.size_bytes);
258
259 dev_log!(
260 "wasm",
261 "Memory deallocated successfully. Remaining usage: {} MB",
262 self.current_usage_mb()
263 );
264
265 Ok(true)
266 } else {
267 dev_log!(
268 "wasm",
269 "warn: memory allocation not found: {} for instance {}",
270 memory_id,
271 instance_id
272 );
273 Ok(false)
274 }
275 }
276
277 pub fn deallocate_all_for_instance(&mut self, instance_id:&str) -> usize {
279 dev_log!("wasm", "Deallocating all memory for instance {}", instance_id);
280
281 let initial_count = self.allocations.len();
282
283 self.allocations.retain(|a| a.instance_id != instance_id);
284
285 let deallocated_count = initial_count - self.allocations.len();
286
287 if deallocated_count > 0 {
288 dev_log!(
289 "wasm",
290 "Deallocated {} memory allocations for instance {}",
291 deallocated_count,
292 instance_id
293 );
294 }
295
296 deallocated_count
297 }
298
299 pub fn grow_memory(&mut self, instance_id:&str, memory_id:&str, additional_bytes:u64) -> Result<u64> {
301 dev_log!(
302 "wasm",
303 "Growing memory {} for instance {} by {} bytes",
304 memory_id,
305 instance_id,
306 additional_bytes
307 );
308
309 let current_usage = self.current_usage_bytes();
311
312 let allocation = self
313 .allocations
314 .iter_mut()
315 .find(|a| a.instance_id == instance_id && a.id == memory_id)
316 .ok_or_else(|| anyhow::anyhow!("Memory allocation not found"))?;
317
318 self.limits
320 .validate_request(additional_bytes, current_usage)
321 .context("Memory growth validation failed")?;
322
323 allocation.size_bytes += additional_bytes;
324
325 dev_log!("wasm", "Memory grown successfully. New size: {} bytes", allocation.size_bytes);
326
327 Ok(allocation.size_bytes)
328 }
329
330 pub fn get_allocations_for_instance(&self, instance_id:&str) -> Vec<&MemoryAllocation> {
332 self.allocations.iter().filter(|a| a.instance_id == instance_id).collect()
333 }
334
335 pub fn is_exceeded(&self) -> bool { self.current_usage_bytes() > self.limits.max_memory_bytes() }
337
338 pub fn usage_percentage(&self) -> f64 {
340 (self.current_usage_bytes() as f64 / self.limits.max_memory_bytes() as f64) * 100.0
341 }
342
343 pub fn reset(&mut self) {
345 self.allocations.clear();
346 self.stats = Arc::new(MemoryStats::default());
347 self.peak_usage.store(0, Ordering::Relaxed);
348 dev_log!("wasm", "Memory manager reset");
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_memory_limits_default() {
358 let limits = MemoryLimits::default();
359 assert_eq!(limits.max_memory_mb, 512);
360 assert_eq!(limits.initial_memory_mb, 64);
361 }
362
363 #[test]
364 fn test_memory_limits_custom() {
365 let limits = MemoryLimits::new(1024, 128, 50);
366 assert_eq!(limits.max_memory_mb, 1024);
367 assert_eq!(limits.initial_memory_mb, 128);
368 assert_eq!(limits.max_instances, 50);
369 }
370
371 #[test]
372 fn test_memory_limits_validation() {
373 let limits = MemoryLimits::new(100, 10, 10);
374
375 assert!(limits.validate_request(50, 0).is_ok());
377
378 assert!(limits.validate_request(150, 0).is_err());
380 assert!(limits.validate_request(50, 60).is_err());
381 }
382
383 #[test]
384 fn test_memory_manager_creation() {
385 let limits = MemoryLimits::default();
386 let manager = MemoryManagerImpl::new(limits);
387 assert_eq!(manager.current_usage_bytes(), 0);
388 assert_eq!(manager.allocations.len(), 0);
389 }
390
391 #[test]
392 fn test_memory_allocation() {
393 let limits = MemoryLimits::default();
394 let mut manager = MemoryManagerImpl::new(limits);
395
396 let result = manager.allocate_memory("test-instance", "heap", 1024);
397 assert!(result.is_ok());
398 assert_eq!(manager.current_usage_bytes(), 1024);
399 assert_eq!(manager.allocations.len(), 1);
400 }
401
402 #[test]
403 fn test_memory_deallocation() {
404 let limits = MemoryLimits::default();
405 let mut manager = MemoryManagerImpl::new(limits);
406
407 manager.allocate_memory("test-instance", "heap", 1024).unwrap();
408 let allocation = &manager.allocations[0];
409 let memory_id = allocation.id.clone();
410
411 let result = manager.deallocate_memory("test-instance", &memory_id);
412 assert!(result.is_ok());
413 assert_eq!(manager.current_usage_bytes(), 0);
414 assert_eq!(manager.allocations.len(), 0);
415 }
416
417 #[test]
418 fn test_memory_stats() {
419 let mut stats = MemoryStats::default();
420 stats.record_allocation(1024);
421 assert_eq!(stats.allocation_count, 1);
422 assert_eq!(stats.total_allocated, 1024);
423
424 stats.record_deallocation(512);
425 assert_eq!(stats.deallocation_count, 1);
426 assert_eq!(stats.total_allocated, 512);
427 }
428
429 #[test]
430 fn test_memory_usage_percentage() {
431 let limits = MemoryLimits::new(1000, 0, 0);
432 let mut manager = MemoryManagerImpl::new(limits);
433
434 manager.allocate_memory("test", "heap", 500).unwrap();
435 assert_eq!(manager.usage_percentage(), 50.0);
436 }
437}