use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct ResourceLimits {
pub max_memory_bytes: Option<usize>,
pub max_cpu_seconds: Option<u64>,
pub max_wall_time_seconds: Option<u64>,
pub max_file_descriptors: Option<usize>,
}
impl ResourceLimits {
pub fn unlimited() -> Self {
Self {
max_memory_bytes: None,
max_cpu_seconds: None,
max_wall_time_seconds: None,
max_file_descriptors: None,
}
}
pub fn memory_constrained(max_memory_mb: usize) -> Self {
Self {
max_memory_bytes: Some(max_memory_mb * 1024 * 1024),
max_cpu_seconds: None,
max_wall_time_seconds: Some(300), max_file_descriptors: Some(100),
}
}
pub fn cpu_intensive(max_cpu_seconds: u64) -> Self {
Self {
max_memory_bytes: None,
max_cpu_seconds: Some(max_cpu_seconds),
max_wall_time_seconds: Some(max_cpu_seconds + 60),
max_file_descriptors: Some(50),
}
}
pub fn io_intensive(max_wall_time_seconds: u64) -> Self {
Self {
max_memory_bytes: Some(512 * 1024 * 1024), max_cpu_seconds: None,
max_wall_time_seconds: Some(max_wall_time_seconds),
max_file_descriptors: Some(1000),
}
}
pub fn with_max_memory_mb(mut self, mb: usize) -> Self {
self.max_memory_bytes = Some(mb * 1024 * 1024);
self
}
pub fn with_max_cpu_seconds(mut self, seconds: u64) -> Self {
self.max_cpu_seconds = Some(seconds);
self
}
pub fn with_max_wall_time_seconds(mut self, seconds: u64) -> Self {
self.max_wall_time_seconds = Some(seconds);
self
}
}
#[derive(Clone)]
pub struct ResourceTracker {
start_time: Arc<Mutex<Instant>>,
peak_memory_bytes: Arc<Mutex<usize>>,
limits: ResourceLimits,
}
impl ResourceTracker {
pub fn new(limits: ResourceLimits) -> Self {
Self {
start_time: Arc::new(Mutex::new(Instant::now())),
peak_memory_bytes: Arc::new(Mutex::new(0)),
limits,
}
}
pub fn start(&self) {
*self.start_time.lock().expect("lock should not be poisoned") = Instant::now();
}
pub fn record_memory_usage(&self, bytes: usize) {
let mut peak = self
.peak_memory_bytes
.lock()
.expect("lock should not be poisoned");
if bytes > *peak {
*peak = bytes;
}
}
pub fn check_limits(&self) -> Result<(), String> {
let elapsed = self
.start_time
.lock()
.expect("lock should not be poisoned")
.elapsed();
if let Some(max_wall_time) = self.limits.max_wall_time_seconds {
if elapsed > Duration::from_secs(max_wall_time) {
return Err(format!(
"Wall-clock time limit exceeded: {}s > {}s",
elapsed.as_secs(),
max_wall_time
));
}
}
if let Some(max_memory) = self.limits.max_memory_bytes {
let peak_memory = *self
.peak_memory_bytes
.lock()
.expect("lock should not be poisoned");
if peak_memory > max_memory {
return Err(format!(
"Memory limit exceeded: {} bytes > {} bytes",
peak_memory, max_memory
));
}
}
Ok(())
}
pub fn elapsed(&self) -> Duration {
self.start_time
.lock()
.expect("lock should not be poisoned")
.elapsed()
}
pub fn peak_memory_bytes(&self) -> usize {
*self
.peak_memory_bytes
.lock()
.expect("lock should not be poisoned")
}
pub fn limits(&self) -> &ResourceLimits {
&self.limits
}
}
pub struct ResourcePool<T> {
resources: Arc<Mutex<Vec<T>>>,
max_size: usize,
}
impl<T> ResourcePool<T> {
pub fn new(max_size: usize) -> Self {
Self {
resources: Arc::new(Mutex::new(Vec::with_capacity(max_size))),
max_size,
}
}
pub fn acquire(&self) -> Option<T> {
self.resources
.lock()
.expect("lock should not be poisoned")
.pop()
}
pub fn release(&self, resource: T) -> Result<(), String> {
let mut resources = self.resources.lock().expect("lock should not be poisoned");
if resources.len() >= self.max_size {
return Err("Resource pool is full".to_string());
}
resources.push(resource);
Ok(())
}
pub fn available(&self) -> usize {
self.resources
.lock()
.expect("lock should not be poisoned")
.len()
}
pub fn max_size(&self) -> usize {
self.max_size
}
pub fn is_empty(&self) -> bool {
self.resources
.lock()
.expect("lock should not be poisoned")
.is_empty()
}
}
impl<T> Clone for ResourcePool<T> {
fn clone(&self) -> Self {
Self {
resources: Arc::clone(&self.resources),
max_size: self.max_size,
}
}
}