Skip to content

Commit

Permalink
feat(rsjudge-runner): 🚧 boilerplate for resource limitation
Browse files Browse the repository at this point in the history
  • Loading branch information
Jisu-Woniu committed Apr 30, 2024
1 parent ff6d661 commit ad15fa8
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 67 deletions.
2 changes: 1 addition & 1 deletion crates/rsjudge-runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ pub use run_as::RunAs;

pub mod user;

mod timing;
mod resources;
94 changes: 94 additions & 0 deletions crates/rsjudge-runner/src/resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0

use std::{
process::Output,
time::{Duration, Instant},
};

use async_trait::async_trait;
use nix::sys::resource::{setrlimit, Resource, Usage};
use tokio::{process::Command, time::sleep};

pub struct ResourceLimit {
/// CPU time limit.
cpu_time_limit: Option<Duration>,
/// Wall time limit.
///
/// # Note
///
/// Wall time limit may be inaccurate, due to the implementation of "wait-and-check" strategy.
wall_time_limit: Option<Duration>,
/// The memory limit **in bytes**.
memory_limit: Option<u64>,
}

#[async_trait]
pub trait RunWithResourceLimit {
/// Run [`Self`] with optional resource limit.
///
/// Before running the command, please setup the command, especially the stdio of the command.
async fn run_with_resource_limit(
&mut self,
resource_info: ResourceLimit,
) -> crate::Result<(Output, Usage)>;
}

#[async_trait]
impl RunWithResourceLimit for Command {
async fn run_with_resource_limit(
&mut self,
resource_info: ResourceLimit,
) -> crate::Result<(Output, Usage)> {
if let Some(cpu_time_limit) = resource_info.cpu_time_limit {
let set_cpu_limit = move || {
setrlimit(
Resource::RLIMIT_CPU,
cpu_time_limit.as_secs(),
cpu_time_limit.as_secs(),
)?;

Ok(())
};
unsafe {
self.pre_exec(set_cpu_limit);
}
}
if let Some(memory_limit) = resource_info.memory_limit {
let set_memory_limit = move || {
setrlimit(Resource::RLIMIT_AS, memory_limit, memory_limit)?;

Ok(())
};
unsafe {
self.pre_exec(set_memory_limit);
}
}
let mut child = self.spawn()?;
let start = Instant::now();
if let Some(wall_time_limit) = resource_info.wall_time_limit {
loop {
let elapsed = start.elapsed();
if elapsed >= wall_time_limit {
child.kill().await?;
break Err(crate::Error::TimeLimitExceeded {
cpu_time: None,
wall_time: Some(elapsed),
});
}
if child.try_wait()?.is_some() {
todo!(
"Wait the child with a wrapped version of wait4: {:?}",
child.wait().await?
);
// Double check the time usage.
}
sleep(Duration::from_millis(10)).await;
}
} else {
todo!(
"Wait the child with a wrapped version of wait4: {:?}",
child.wait().await?
);
}
}
}
66 changes: 0 additions & 66 deletions crates/rsjudge-runner/src/timing.rs

This file was deleted.

0 comments on commit ad15fa8

Please sign in to comment.