Skip to content

Commit

Permalink
refactor(Judger): 🚚 move From<CpuAcct(cgroups_rs)> for Cpu logic to…
Browse files Browse the repository at this point in the history
… crate::sandbox::limiter::stat(where Cpu reside)
  • Loading branch information
Eason0729 committed Apr 19, 2024
1 parent f932f10 commit f1e3c4e
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 185 deletions.
3 changes: 0 additions & 3 deletions judger/src/sandbox/limiter/hier.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use cgroups_rs::*;
use std::time::Duration;

pub const MONITOR_ACCURACY: Duration = Duration::from_millis(80);

pub enum MonitorKind {
Cpu,
Expand Down
53 changes: 37 additions & 16 deletions judger/src/sandbox/limiter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//! Provide ability to limit resource and retrieve final cpu and memory usage
//!
//! To use this module, you need to create it (provide resource limitation) and mount it,
//! finally, spawn process(it's user's responsibility to ensure the process
//! is spawned within the cgroup)
pub(self) mod hier;
pub(self) mod stat;
pub(self) mod wrapper;
Expand All @@ -10,6 +15,9 @@ use hier::*;
use std::sync::Arc;
use tokio::time::*;

const MONITOR_ACCURACY: Duration = Duration::from_millis(80);

/// Exit reason of the process
pub enum ExitReason {
TimeOut,
MemoryOut,
Expand All @@ -21,7 +29,7 @@ pub async fn monitor(cgroup: Arc<Cgroup>, cpu: Cpu) -> ExitReason {
let oom_signal = wrapper.oom();

loop {
sleep(MONITOR_ACCURACY/2).await;
sleep(MONITOR_ACCURACY / 2).await;

if let Ok(oom_hint) = oom_signal.try_recv() {
log::trace!("oom hint: {}", oom_hint);
Expand All @@ -46,16 +54,10 @@ impl Drop for Limiter {
if let Some(monitor_task) = &self.monitor_task {
monitor_task.abort();
}
if MONITER_KIND.heir().v2() {
self.cgroup.kill().expect("cgroup.kill does not exist");
} else {
// use rustix::process::*;
// pid should not be reused until SIGPIPE send(when Process is Drop)
// therefore, it is safe to try killing the process(only true for nsjail)

// current implementation of v1 support do nothing, wait for action of cleaning
// up the process on drop
// for pid in self.cgroup.tasks() {}
debug_assert!(self.cgroup.tasks().is_empty());
match self.cgroup.tasks().is_empty() {
true => log::warn!("cgroup still have process running"),
false => self.cgroup.delete().expect("cgroup cannot be deleted"),
}
}
}
Expand All @@ -71,7 +73,7 @@ impl Limiter {
.memory_swap_limit(0)
.done()
.cpu()
.period((MONITOR_ACCURACY/2).as_nanos() as u64)
.period((MONITOR_ACCURACY / 2).as_nanos() as u64)
.quota(MONITOR_ACCURACY.as_nanos() as i64)
.realtime_period(MONITOR_ACCURACY.as_nanos() as u64)
.realtime_runtime(MONITOR_ACCURACY.as_nanos() as i64)
Expand All @@ -87,12 +89,31 @@ impl Limiter {
})
}
/// wait for resource to exhaust
///
/// Please remember that [`Drop::drop`] only optimistic kill(`SIGKILL`)
/// the process inside it,
/// user SHOULD NOT rely on this to kill the process.
///
///
/// 2. Actively limit(notify) cpu resource is achieved by polling the cgroup,
/// the delay require special attention, it is only guaranteed
/// to below limitation provided + [`MONITOR_ACCURACY`].
pub async fn wait_exhaust(&mut self) -> ExitReason {
self.monitor_task.take().unwrap().await.unwrap()
let reason = self.monitor_task.take().unwrap().await.unwrap();
// optimistic kill(`SIGKILL`) the process inside
self.cgroup.kill().expect("cgroup.kill does not exist");
reason
}
/// get the current resource usage
pub async fn stat(self)->(Cpu,Memory){
/// get the final resource usage
///
/// Please remember thatActively limit(notify) cpu resource is achieved
/// by polling the cgroup, therefore the delay requirespecial attention,
/// it is only guaranteed to below limitation provided + [`MONITOR_ACCURACY`].
pub async fn stat(self) -> (Cpu, Memory) {
// there should be no process left
debug_assert!(self.cgroup.tasks().is_empty());
// poll once more to get final stat
let wrapper = wrapper::CgroupWrapper::new(&self.cgroup);
(wrapper.cpu(),wrapper.memory())
(wrapper.cpu(), wrapper.memory())
}
}
44 changes: 44 additions & 0 deletions judger/src/sandbox/limiter/stat.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use cgroups_rs::cpuacct::CpuAcct;

pub struct Memory {
pub kernel: u64,
pub user: u64,
Expand All @@ -14,4 +16,46 @@ impl Cpu {
pub(super) fn out_of_resources(resource: &Self, stat: Self) -> bool {
stat.kernel > resource.kernel || stat.user > resource.user || stat.total > resource.total
}

pub(super) fn from_acct(acct: CpuAcct) -> Self {
Cpu {
kernel: acct.usage_sys,
user: acct.usage_user,
total: acct.usage,
}
}
pub(super) fn from_raw(raw: &str) -> Self {
let mut kernel = u64::MAX;
let mut user = u64::MAX;
let mut total = u64::MAX;

for (key, value) in raw.split('\n').filter_map(|stmt| stmt.split_once(' ')) {
match key {
"usage_usec" => total = value.parse().unwrap(),
"user_usec" => user = value.parse().unwrap(),
"system_usec" => kernel = value.parse().unwrap(),
_ => {}
};
}

Self {
kernel,
user,
total,
}
}
}

#[cfg(test)]
mod test {
use super::*;
#[test]
/// Test the [`Cpu::from_raw`] function
fn cpu_from_raw() {
let raw = "usage_usec 158972260000\nuser_usec 115998852000\nsystem_usec 42973408000\ncore_sched.force_idle_usec 0\nnr_periods 0\nnr_throttled 0\nthrottled_usec 0\nnr_bursts 0\nburst_usec 0\n";
let cpu = Cpu::from_raw(raw);
assert_eq!(cpu.kernel, 158972260000);
assert_eq!(cpu.user, 115998852000);
assert_eq!(cpu.total, 42973408000);
}
}
38 changes: 9 additions & 29 deletions judger/src/sandbox/limiter/wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use cgroups_rs::{cpu::CpuController, cpuacct::CpuAcctController, memory::MemController, Cgroup};

use super::hier::*;
use super::stat::*;
use cgroups_rs::{cpu::CpuController, cpuacct::CpuAcctController, memory::MemController, Cgroup};
use std::ops::Deref;

/// newtype wrapper for cgroup form cgroup_rs
pub struct CgroupWrapper<'a> {
Expand All @@ -12,38 +13,17 @@ impl<'a> CgroupWrapper<'a> {
Self { cgroup }
}
pub fn cpu(&self) -> Cpu {
let mut kernel = u64::MAX;
let mut user = u64::MAX;
let mut total = u64::MAX;

match self.cgroup.controller_of::<CpuAcctController>() {
Some(controller) => {
let usage = controller.cpuacct();
kernel = usage.usage_sys;
user = usage.usage_user;
total = usage.usage;
match MONITER_KIND.deref() {
MonitorKind::Cpu => {
let controller: &CpuAcctController = self.cgroup.controller_of().unwrap();
Cpu::from_acct(controller.cpuacct())
}
None => {
MonitorKind::CpuAcct => {
let controller: &CpuController = self.cgroup.controller_of().unwrap();

let raw: &str = &controller.cpu().stat;

for (key, value) in raw.split('\n').filter_map(|stmt| stmt.split_once(' ')) {
match key {
"usage_usec" => total = value.parse().unwrap(),
"user_usec" => user = value.parse().unwrap(),
"system_usec" => kernel = value.parse().unwrap(),
_ => {}
};
}
Cpu::from_raw(raw)
}
}

Cpu {
kernel,
user,
total,
}
}
pub fn oom(&self) -> std::sync::mpsc::Receiver<String> {
let controller = self.cgroup.controller_of::<MemController>().unwrap();
Expand Down
Empty file removed judger/src/sandbox/resource.rs
Empty file.
137 changes: 0 additions & 137 deletions judger/src/sandbox/semaphore.rs

This file was deleted.

0 comments on commit f1e3c4e

Please sign in to comment.