1use std::sync::{
7 Arc,
8 atomic::{AtomicU64, Ordering},
9};
10
11use anyhow::{Context, Result};
12use serde::{Deserialize, Serialize};
13use tracing::{debug, instrument, warn};
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 #[instrument(skip(self, instance_id))]
188 pub fn allocate_memory(&mut self, instance_id:&str, memory_type:&str, requested_bytes:u64) -> Result<u64> {
189 debug!(
190 "Allocating {} bytes for instance {} (type: {})",
191 requested_bytes, instance_id, memory_type
192 );
193
194 let current_usage = self.current_usage_bytes();
195
196 self.limits
198 .validate_request(requested_bytes, current_usage)
199 .context("Memory allocation validation failed")?;
200
201 if self.allocations.len() >= self.limits.max_memories {
203 return Err(anyhow::anyhow!(
204 "Maximum number of memory allocations reached: {}",
205 self.limits.max_memories
206 ));
207 }
208
209 let allocation = MemoryAllocation {
211 id:format!("alloc-{}", uuid::Uuid::new_v4()),
212 instance_id:instance_id.to_string(),
213 memory_type:memory_type.to_string(),
214 size_bytes:requested_bytes,
215 max_size_bytes:self.limits.max_memory_bytes() - current_usage,
216 allocated_at:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs(),
217 is_shared:false,
218 };
219
220 self.allocations.push(allocation);
221
222 Arc::make_mut(&mut self.stats).record_allocation(requested_bytes);
224
225 let new_peak = self.current_usage_bytes();
227 let current_peak = self.peak_usage.load(Ordering::Relaxed);
228 if new_peak > current_peak {
229 self.peak_usage.store(new_peak, Ordering::Relaxed);
230 }
231
232 debug!("Memory allocated successfully. Total usage: {} MB", self.current_usage_mb());
233
234 Ok(requested_bytes)
235 }
236
237 #[instrument(skip(self, instance_id))]
239 pub fn deallocate_memory(&mut self, instance_id:&str, memory_id:&str) -> Result<bool> {
240 debug!("Deallocating memory {} for instance {}", memory_id, instance_id);
241
242 let pos = self
243 .allocations
244 .iter()
245 .position(|a| a.instance_id == instance_id && a.id == memory_id);
246
247 if let Some(pos) = pos {
248 let allocation = self.allocations.remove(pos);
249
250 Arc::make_mut(&mut self.stats).record_deallocation(allocation.size_bytes);
252
253 debug!(
254 "Memory deallocated successfully. Remaining usage: {} MB",
255 self.current_usage_mb()
256 );
257
258 Ok(true)
259 } else {
260 warn!("Memory allocation not found: {} for instance {}", memory_id, instance_id);
261 Ok(false)
262 }
263 }
264
265 #[instrument(skip(self, instance_id))]
267 pub fn deallocate_all_for_instance(&mut self, instance_id:&str) -> usize {
268 debug!("Deallocating all memory for instance {}", instance_id);
269
270 let initial_count = self.allocations.len();
271
272 self.allocations.retain(|a| a.instance_id != instance_id);
273
274 let deallocated_count = initial_count - self.allocations.len();
275
276 if deallocated_count > 0 {
277 debug!(
278 "Deallocated {} memory allocations for instance {}",
279 deallocated_count, instance_id
280 );
281 }
282
283 deallocated_count
284 }
285
286 #[instrument(skip(self, instance_id, memory_id))]
288 pub fn grow_memory(&mut self, instance_id:&str, memory_id:&str, additional_bytes:u64) -> Result<u64> {
289 debug!(
290 "Growing memory {} for instance {} by {} bytes",
291 memory_id, instance_id, additional_bytes
292 );
293
294 let current_usage = self.current_usage_bytes();
296
297 let allocation = self
298 .allocations
299 .iter_mut()
300 .find(|a| a.instance_id == instance_id && a.id == memory_id)
301 .ok_or_else(|| anyhow::anyhow!("Memory allocation not found"))?;
302
303 self.limits
305 .validate_request(additional_bytes, current_usage)
306 .context("Memory growth validation failed")?;
307
308 allocation.size_bytes += additional_bytes;
309
310 debug!("Memory grown successfully. New size: {} bytes", allocation.size_bytes);
311
312 Ok(allocation.size_bytes)
313 }
314
315 pub fn get_allocations_for_instance(&self, instance_id:&str) -> Vec<&MemoryAllocation> {
317 self.allocations.iter().filter(|a| a.instance_id == instance_id).collect()
318 }
319
320 pub fn is_exceeded(&self) -> bool { self.current_usage_bytes() > self.limits.max_memory_bytes() }
322
323 pub fn usage_percentage(&self) -> f64 {
325 (self.current_usage_bytes() as f64 / self.limits.max_memory_bytes() as f64) * 100.0
326 }
327
328 pub fn reset(&mut self) {
330 self.allocations.clear();
331 self.stats = Arc::new(MemoryStats::default());
332 self.peak_usage.store(0, Ordering::Relaxed);
333 debug!("Memory manager reset");
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn test_memory_limits_default() {
343 let limits = MemoryLimits::default();
344 assert_eq!(limits.max_memory_mb, 512);
345 assert_eq!(limits.initial_memory_mb, 64);
346 }
347
348 #[test]
349 fn test_memory_limits_custom() {
350 let limits = MemoryLimits::new(1024, 128, 50);
351 assert_eq!(limits.max_memory_mb, 1024);
352 assert_eq!(limits.initial_memory_mb, 128);
353 assert_eq!(limits.max_instances, 50);
354 }
355
356 #[test]
357 fn test_memory_limits_validation() {
358 let limits = MemoryLimits::new(100, 10, 10);
359
360 assert!(limits.validate_request(50, 0).is_ok());
362
363 assert!(limits.validate_request(150, 0).is_err());
365 assert!(limits.validate_request(50, 60).is_err());
366 }
367
368 #[test]
369 fn test_memory_manager_creation() {
370 let limits = MemoryLimits::default();
371 let manager = MemoryManagerImpl::new(limits);
372 assert_eq!(manager.current_usage_bytes(), 0);
373 assert_eq!(manager.allocations.len(), 0);
374 }
375
376 #[test]
377 fn test_memory_allocation() {
378 let limits = MemoryLimits::default();
379 let mut manager = MemoryManagerImpl::new(limits);
380
381 let result = manager.allocate_memory("test-instance", "heap", 1024);
382 assert!(result.is_ok());
383 assert_eq!(manager.current_usage_bytes(), 1024);
384 assert_eq!(manager.allocations.len(), 1);
385 }
386
387 #[test]
388 fn test_memory_deallocation() {
389 let limits = MemoryLimits::default();
390 let mut manager = MemoryManagerImpl::new(limits);
391
392 manager.allocate_memory("test-instance", "heap", 1024).unwrap();
393 let allocation = &manager.allocations[0];
394 let memory_id = allocation.id.clone();
395
396 let result = manager.deallocate_memory("test-instance", &memory_id);
397 assert!(result.is_ok());
398 assert_eq!(manager.current_usage_bytes(), 0);
399 assert_eq!(manager.allocations.len(), 0);
400 }
401
402 #[test]
403 fn test_memory_stats() {
404 let mut stats = MemoryStats::default();
405 stats.record_allocation(1024);
406 assert_eq!(stats.allocation_count, 1);
407 assert_eq!(stats.total_allocated, 1024);
408
409 stats.record_deallocation(512);
410 assert_eq!(stats.deallocation_count, 1);
411 assert_eq!(stats.total_allocated, 512);
412 }
413
414 #[test]
415 fn test_memory_usage_percentage() {
416 let limits = MemoryLimits::new(1000, 0, 0);
417 let mut manager = MemoryManagerImpl::new(limits);
418
419 manager.allocate_memory("test", "heap", 500).unwrap();
420 assert_eq!(manager.usage_percentage(), 50.0);
421 }
422}