1use std::sync::atomic::{AtomicU64, Ordering};
4use std::sync::{Arc, Mutex};
5use std::time::{Duration, Instant};
6
7use crate::error::{Error, Result};
8use crate::security::{
9 MemoryLimits, CpuLimits, IoLimits, TimeLimits, ResourceLimits
10};
11
12#[derive(Debug, Clone)]
14pub struct MemoryResourceTracker {
15 pub max_memory_pages: u32,
17
18 current_pages: Arc<AtomicU64>,
20
21 peak_pages: Arc<AtomicU64>,
23
24 growth_tracker: Arc<Mutex<MemoryGrowthTracker>>,
26}
27
28#[derive(Debug)]
30struct MemoryGrowthTracker {
31 max_rate: Option<u32>,
33
34 last_size: u64,
36
37 last_check: Instant,
39
40 growth_events: Vec<(Instant, u64)>,
42
43 window: Duration,
45}
46
47impl MemoryResourceTracker {
48 pub fn new(limits: &MemoryLimits) -> Self {
50 let growth_tracker = MemoryGrowthTracker {
51 max_rate: limits.max_growth_rate,
52 last_size: 0,
53 last_check: Instant::now(),
54 growth_events: Vec::new(),
55 window: Duration::from_secs(1), };
57
58 Self {
59 max_memory_pages: limits.max_memory_pages,
60 current_pages: Arc::new(AtomicU64::new(limits.reserved_memory_pages as u64)),
61 peak_pages: Arc::new(AtomicU64::new(limits.reserved_memory_pages as u64)),
62 growth_tracker: Arc::new(Mutex::new(growth_tracker)),
63 }
64 }
65
66 pub fn check_allocation(&self, pages: u32) -> Result<()> {
68 let current = self.current_pages.load(Ordering::Acquire);
69 let requested = current + pages as u64;
70
71 if requested > self.max_memory_pages as u64 {
72 return Err(Error::ResourceLimit {
73 message: format!("Memory allocation of {} pages would exceed limit of {} pages",
74 pages, self.max_memory_pages)
75 });
76 }
77
78 if let Some(max_rate) = self.growth_tracker.lock().unwrap().max_rate {
80 let now = Instant::now();
81 let mut tracker = self.growth_tracker.lock().unwrap();
82
83 let cutoff = now - tracker.window;
85 tracker.growth_events.retain(|(time, _)| *time >= cutoff);
86
87 tracker.growth_events.push((now, pages as u64));
89
90 let total_growth: u64 = tracker.growth_events.iter().map(|(_, size)| *size).sum();
92
93 if total_growth > max_rate as u64 {
94 return Err(Error::ResourceLimit {
95 message: format!("Memory growth rate of {} pages/s exceeds limit of {} pages/s",
96 total_growth, max_rate)
97 });
98 }
99
100 tracker.last_size = requested;
101 tracker.last_check = now;
102 }
103
104 Ok(())
105 }
106
107 pub fn update(&self, pages: u32) {
109 let current = self.current_pages.fetch_add(pages as u64, Ordering::AcqRel) + pages as u64;
110 let mut peak = self.peak_pages.load(Ordering::Acquire);
111
112 while current > peak {
113 match self.peak_pages.compare_exchange_weak(
114 peak,
115 current,
116 Ordering::AcqRel,
117 Ordering::Acquire,
118 ) {
119 Ok(_) => break,
120 Err(actual) => peak = actual,
121 }
122 }
123 }
124
125 pub fn current_pages(&self) -> u64 {
127 self.current_pages.load(Ordering::Acquire)
128 }
129
130 pub fn peak_pages(&self) -> u64 {
132 self.peak_pages.load(Ordering::Acquire)
133 }
134
135 pub fn reset_peak(&self) {
137 self.peak_pages.store(
138 self.current_pages.load(Ordering::Acquire),
139 Ordering::Release
140 );
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct CpuResourceTracker {
147 pub max_execution_time: Duration,
149
150 pub cpu_usage_percentage: Option<u8>,
152
153 pub max_threads: Option<u32>,
155
156 start_time: Arc<Mutex<Option<Instant>>>,
158
159 total_time: Arc<AtomicU64>,
161
162 active_threads: Arc<AtomicU64>,
164}
165
166impl CpuResourceTracker {
167 pub fn new(limits: &CpuLimits) -> Self {
169 Self {
170 max_execution_time: Duration::from_millis(limits.max_execution_time_ms),
171 cpu_usage_percentage: limits.cpu_usage_percentage,
172 max_threads: limits.max_threads,
173 start_time: Arc::new(Mutex::new(None)),
174 total_time: Arc::new(AtomicU64::new(0)),
175 active_threads: Arc::new(AtomicU64::new(0)),
176 }
177 }
178
179 pub fn start_execution(&self) {
181 let mut start = self.start_time.lock().unwrap();
182 if start.is_none() {
183 *start = Some(Instant::now());
184 }
185 }
186
187 pub fn stop_execution(&self) {
189 let mut start_lock = self.start_time.lock().unwrap();
190 if let Some(start) = *start_lock {
191 let elapsed = start.elapsed();
192 self.total_time.fetch_add(elapsed.as_millis() as u64, Ordering::AcqRel);
193 *start_lock = None;
194 }
195 }
196
197 pub fn check_time_limit(&self) -> Result<()> {
199 let total = self.total_time.load(Ordering::Acquire);
200
201 let mut current_total = total;
203 let start_lock = self.start_time.lock().unwrap();
204 if let Some(start) = *start_lock {
205 current_total += start.elapsed().as_millis() as u64;
206 }
207
208 if current_total > self.max_execution_time.as_millis() as u64 {
209 return Err(Error::Timeout {
210 operation: "execution".to_string(),
211 duration: Duration::from_millis(current_total),
212 instance_id: None,
213 });
214 }
215
216 Ok(())
217 }
218
219 pub fn register_thread(&self) -> Result<()> {
221 if let Some(max) = self.max_threads {
222 let current = self.active_threads.fetch_add(1, Ordering::AcqRel) + 1;
223 if current > max as u64 {
224 self.active_threads.fetch_sub(1, Ordering::AcqRel);
226 return Err(Error::ResourceLimit {
227 message: format!("Thread limit of {} exceeded", max)
228 });
229 }
230 } else {
231 self.active_threads.fetch_add(1, Ordering::AcqRel);
232 }
233
234 Ok(())
235 }
236
237 pub fn unregister_thread(&self) {
239 self.active_threads.fetch_sub(1, Ordering::AcqRel);
240 }
241
242 pub fn total_time_ms(&self) -> u64 {
244 let total = self.total_time.load(Ordering::Acquire);
245
246 let mut current_total = total;
248 let start_lock = self.start_time.lock().unwrap();
249 if let Some(start) = *start_lock {
250 current_total += start.elapsed().as_millis() as u64;
251 }
252
253 current_total
254 }
255
256 pub fn active_threads(&self) -> u32 {
258 self.active_threads.load(Ordering::Acquire) as u32
259 }
260
261 pub fn apply_throttling(&self) {
263 if let Some(percentage) = self.cpu_usage_percentage {
264 if percentage >= 100 {
265 return; }
267
268 if percentage > 0 {
270 let sleep_time_ns = (100 - percentage) as u64 * 10_000; std::thread::sleep(Duration::from_nanos(sleep_time_ns));
273 }
274 }
275 }
276}
277
278#[derive(Debug, Clone)]
280pub struct IoResourceTracker {
281 pub max_open_files: u32,
283
284 pub max_read_bytes_per_second: Option<u64>,
286
287 pub max_write_bytes_per_second: Option<u64>,
289
290 pub max_total_read_bytes: Option<u64>,
292
293 pub max_total_write_bytes: Option<u64>,
295
296 open_files: Arc<AtomicU64>,
298
299 total_read: Arc<AtomicU64>,
301
302 total_write: Arc<AtomicU64>,
304
305 rate_tracker: Arc<Mutex<IoRateTracker>>,
307}
308
309#[derive(Debug)]
311struct IoRateTracker {
312 read_events: Vec<(Instant, u64)>,
314
315 write_events: Vec<(Instant, u64)>,
317
318 window: Duration,
320}
321
322impl IoResourceTracker {
323 pub fn new(limits: &IoLimits) -> Self {
325 let rate_tracker = IoRateTracker {
326 read_events: Vec::new(),
327 write_events: Vec::new(),
328 window: Duration::from_secs(1), };
330
331 Self {
332 max_open_files: limits.max_open_files,
333 max_read_bytes_per_second: limits.max_read_bytes_per_second,
334 max_write_bytes_per_second: limits.max_write_bytes_per_second,
335 max_total_read_bytes: limits.max_total_read_bytes,
336 max_total_write_bytes: limits.max_total_write_bytes,
337 open_files: Arc::new(AtomicU64::new(0)),
338 total_read: Arc::new(AtomicU64::new(0)),
339 total_write: Arc::new(AtomicU64::new(0)),
340 rate_tracker: Arc::new(Mutex::new(rate_tracker)),
341 }
342 }
343
344 pub fn register_open(&self) -> Result<()> {
346 let current = self.open_files.fetch_add(1, Ordering::AcqRel) + 1;
347 if current > self.max_open_files as u64 {
348 self.open_files.fetch_sub(1, Ordering::AcqRel);
350 return Err(Error::ResourceLimit {
351 message: format!("Open file limit of {} exceeded", self.max_open_files)
352 });
353 }
354
355 Ok(())
356 }
357
358 pub fn register_close(&self) {
360 self.open_files.fetch_sub(1, Ordering::AcqRel);
361 }
362
363 pub fn register_read(&self, bytes: u64) -> Result<()> {
365 let total = self.total_read.fetch_add(bytes, Ordering::AcqRel) + bytes;
367
368 if let Some(limit) = self.max_total_read_bytes {
370 if total > limit {
371 return Err(Error::ResourceLimit {
372 message: format!("Total read limit of {} bytes exceeded", limit)
373 });
374 }
375 }
376
377 if let Some(rate_limit) = self.max_read_bytes_per_second {
379 let now = Instant::now();
380 let mut tracker = self.rate_tracker.lock().unwrap();
381
382 let cutoff = now - tracker.window;
384 tracker.read_events.retain(|(time, _)| *time >= cutoff);
385
386 tracker.read_events.push((now, bytes));
388
389 let window_total: u64 = tracker.read_events.iter().map(|(_, size)| *size).sum();
391
392 if window_total > rate_limit {
393 return Err(Error::ResourceLimit {
394 message: format!("Read rate limit of {} bytes/s exceeded", rate_limit)
395 });
396 }
397 }
398
399 Ok(())
400 }
401
402 pub fn register_write(&self, bytes: u64) -> Result<()> {
404 let total = self.total_write.fetch_add(bytes, Ordering::AcqRel) + bytes;
406
407 if let Some(limit) = self.max_total_write_bytes {
409 if total > limit {
410 return Err(Error::ResourceLimit {
411 message: format!("Total write limit of {} bytes exceeded", limit)
412 });
413 }
414 }
415
416 if let Some(rate_limit) = self.max_write_bytes_per_second {
418 let now = Instant::now();
419 let mut tracker = self.rate_tracker.lock().unwrap();
420
421 let cutoff = now - tracker.window;
423 tracker.write_events.retain(|(time, _)| *time >= cutoff);
424
425 tracker.write_events.push((now, bytes));
427
428 let window_total: u64 = tracker.write_events.iter().map(|(_, size)| *size).sum();
430
431 if window_total > rate_limit {
432 return Err(Error::ResourceLimit {
433 message: format!("Write rate limit of {} bytes/s exceeded", rate_limit)
434 });
435 }
436 }
437
438 Ok(())
439 }
440
441 pub fn open_files(&self) -> u32 {
443 self.open_files.load(Ordering::Acquire) as u32
444 }
445
446 pub fn total_read(&self) -> u64 {
448 self.total_read.load(Ordering::Acquire)
449 }
450
451 pub fn total_write(&self) -> u64 {
453 self.total_write.load(Ordering::Acquire)
454 }
455
456 pub fn read_rate(&self) -> u64 {
458 let tracker = self.rate_tracker.lock().unwrap();
459 let now = Instant::now();
460 let cutoff = now - tracker.window;
461
462 tracker.read_events
464 .iter()
465 .filter(|(time, _)| *time >= cutoff)
466 .map(|(_, size)| *size)
467 .sum()
468 }
469
470 pub fn write_rate(&self) -> u64 {
472 let tracker = self.rate_tracker.lock().unwrap();
473 let now = Instant::now();
474 let cutoff = now - tracker.window;
475
476 tracker.write_events
478 .iter()
479 .filter(|(time, _)| *time >= cutoff)
480 .map(|(_, size)| *size)
481 .sum()
482 }
483}
484
485#[derive(Debug, Clone)]
487pub struct TimeResourceTracker {
488 pub max_total_time: Duration,
490
491 pub max_idle_time: Option<Duration>,
493
494 start_time: Arc<Mutex<Instant>>,
496
497 last_activity: Arc<Mutex<Instant>>,
499}
500
501impl TimeResourceTracker {
502 pub fn new(limits: &TimeLimits) -> Self {
504 let now = Instant::now();
505
506 Self {
507 max_total_time: Duration::from_millis(limits.max_total_time_ms),
508 max_idle_time: limits.max_idle_time_ms.map(Duration::from_millis),
509 start_time: Arc::new(Mutex::new(now)),
510 last_activity: Arc::new(Mutex::new(now)),
511 }
512 }
513
514 pub fn register_activity(&self) {
516 *self.last_activity.lock().unwrap() = Instant::now();
517 }
518
519 pub fn check_limits(&self) -> Result<()> {
521 let now = Instant::now();
522
523 let elapsed = now.duration_since(*self.start_time.lock().unwrap());
525 if elapsed > self.max_total_time {
526 return Err(Error::Timeout {
527 operation: "total time".to_string(),
528 duration: elapsed,
529 instance_id: None,
530 });
531 }
532
533 if let Some(idle_limit) = self.max_idle_time {
535 let idle_time = now.duration_since(*self.last_activity.lock().unwrap());
536 if idle_time > idle_limit {
537 return Err(Error::ResourceLimit {
538 message: format!("Idle time limit of {}ms exceeded", idle_limit.as_millis())
539 });
540 }
541 }
542
543 Ok(())
544 }
545
546 pub fn elapsed_ms(&self) -> u64 {
548 let now = Instant::now();
549 now.duration_since(*self.start_time.lock().unwrap()).as_millis() as u64
550 }
551
552 pub fn idle_ms(&self) -> u64 {
554 let now = Instant::now();
555 now.duration_since(*self.last_activity.lock().unwrap()).as_millis() as u64
556 }
557}
558
559#[derive(Debug, Clone)]
561pub struct ResourceLimitManager {
562 pub memory: MemoryResourceTracker,
564
565 pub cpu: CpuResourceTracker,
567
568 pub io: IoResourceTracker,
570
571 pub time: TimeResourceTracker,
573
574 pub fuel: Option<Arc<AtomicU64>>,
576}
577
578impl ResourceLimitManager {
579 pub fn new(limits: &ResourceLimits) -> Self {
581 let fuel = limits.fuel.map(|f| Arc::new(AtomicU64::new(f)));
582
583 Self {
584 memory: MemoryResourceTracker::new(&limits.memory),
585 cpu: CpuResourceTracker::new(&limits.cpu),
586 io: IoResourceTracker::new(&limits.io),
587 time: TimeResourceTracker::new(&limits.time),
588 fuel,
589 }
590 }
591
592 pub fn check_all_limits(&self) -> Result<()> {
594 self.time.check_limits()?;
596
597 self.cpu.check_time_limit()?;
599
600 if let Some(fuel) = &self.fuel {
602 if fuel.load(Ordering::Acquire) == 0 {
603 return Err(Error::ResourceLimit {
604 message: "Fuel limit exceeded".to_string()
605 });
606 }
607 }
608
609 Ok(())
610 }
611
612 pub fn consume_fuel(&self, amount: u64) -> Result<()> {
614 if let Some(fuel) = &self.fuel {
615 let current = fuel.load(Ordering::Acquire);
616 if current < amount {
617 return Err(Error::ResourceLimit {
618 message: format!("Not enough fuel: requested {}, available {}", amount, current)
619 });
620 }
621
622 fuel.fetch_sub(amount, Ordering::AcqRel);
623 }
624
625 Ok(())
626 }
627
628 pub fn add_fuel(&self, amount: u64) -> Result<()> {
630 if let Some(fuel) = &self.fuel {
631 fuel.fetch_add(amount, Ordering::AcqRel);
632 Ok(())
633 } else {
634 Err(Error::UnsupportedOperation {
635 message: "Fuel metering is not enabled".to_string()
636 })
637 }
638 }
639
640 pub fn reset_fuel(&self, amount: u64) -> Result<()> {
642 if let Some(fuel) = &self.fuel {
643 fuel.store(amount, Ordering::Release);
644 Ok(())
645 } else {
646 Err(Error::UnsupportedOperation {
647 message: "Fuel metering is not enabled".to_string()
648 })
649 }
650 }
651
652 pub fn get_remaining_fuel(&self) -> Option<u64> {
654 self.fuel.as_ref().map(|f| f.load(Ordering::Acquire))
655 }
656
657 pub fn start_monitor(&self) -> std::thread::JoinHandle<()> {
659 let cpu_tracker = self.cpu.clone();
661 let time_tracker = self.time.clone();
662
663 std::thread::spawn(move || {
664 let check_interval = Duration::from_millis(100); loop {
667 std::thread::sleep(check_interval);
669
670 time_tracker.register_activity();
672
673 cpu_tracker.apply_throttling();
675
676 let _ = time_tracker.check_limits();
682 let _ = cpu_tracker.check_time_limit();
683 }
684 })
685 }
686}