Skip to content

Commit

Permalink
feat(Judger): 🚧 draft timelife of a runner
Browse files Browse the repository at this point in the history
  • Loading branch information
Eason0729 committed May 12, 2024
1 parent db5faa9 commit fc7cf4c
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 64 deletions.
2 changes: 1 addition & 1 deletion judger/src/language/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use tokio::{fs::File, sync::Semaphore};
use uuid::Uuid;

use super::config::*;
use super::spec::*;
use crate::{filesystem::Template, CONFIG};

static PLUGIN_PATH: &str = "./plugins";
Expand Down
3 changes: 2 additions & 1 deletion judger/src/language/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod config;
mod daemon;
mod plugin;
mod spec;
mod stage;
75 changes: 15 additions & 60 deletions judger/src/language/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
use std::{
ffi::OsStr,
marker::PhantomData,
path::{Path, PathBuf},
sync::Arc,
};
use std::{path::Path, sync::Arc};

use rustix::path::Arg;
use tokio::fs::{read_dir, File};

use crate::{
filesystem::{Filesystem, Template},
sandbox::{Context as SandboxCtx, Filesystem as SandboxFS, Limit},
};
use crate::filesystem::*;

use super::config::Spec;
use super::{spec::Spec, stage::CompileRunner};
use crate::Result;

static EXTENSION: &str = "lang";

pub async fn load_plugins(path: impl AsRef<Path>) -> std::io::Result<Vec<Plugin>> {
pub async fn load_plugins(path: impl AsRef<Path>) -> Result<Vec<Plugin>> {
let mut plugins = Vec::new();
let mut dir_list = read_dir(path).await?;
while let Some(entry) = dir_list.next_entry().await? {
Expand All @@ -32,65 +25,27 @@ pub async fn load_plugins(path: impl AsRef<Path>) -> std::io::Result<Vec<Plugin>
}

pub struct Plugin {
spec: Spec,
spec: Arc<Spec>,
template: Template<File>,
}

impl Plugin {
pub async fn new(path: impl AsRef<Path> + Clone) -> std::io::Result<Self> {
pub async fn new(path: impl AsRef<Path> + Clone) -> Result<Self> {
let template = Template::new(path.clone()).await?;
let spec_source = template.read_by_path("spec.toml").await.expect(&format!(
"sepc.toml not found in plugin {}",
path.as_ref().display()
));
let spec = Spec::from_str(&spec_source.to_string_lossy());
let spec = Arc::new(Spec::from_str(&spec_source.to_string_lossy()));

Ok(Self { spec, template })
}
pub async fn as_runner(self: Arc<Self>) -> PluginRunner<Compile> {
PluginRunner {
source: self.clone(),
filesystem: self.template.as_filesystem(0),
_stage: PhantomData,
}
}
}

pub struct Compile;
pub struct Execute;

pub struct PluginRunner<S> {
source: Arc<Plugin>,
filesystem: Filesystem<File>,
_stage: PhantomData<S>,
}

impl SandboxCtx for PluginRunner<Compile> {
type FS = PathBuf;

fn create_fs(&mut self) -> Self::FS {
todo!()
}

fn get_args(&mut self) -> impl Iterator<Item = &OsStr> {
self.source
.spec
.compile_command
.iter()
.map(|arg| arg.as_ref())
}
}

impl Limit for PluginRunner<Compile> {
fn get_cpu(&mut self) -> crate::sandbox::Cpu {
todo!()
}

fn get_memory(&mut self) -> crate::sandbox::Memory {
todo!()
}

fn get_output(&mut self) -> u64 {
todo!()
pub async fn as_runner(self: Arc<Self>) -> Result<CompileRunner> {
let filesystem = self
.template
.as_filesystem(self.spec.fs_limit)
.mount()
.await?;
Ok(CompileRunner::new(self.spec.clone(), filesystem))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,24 @@ async fn load_plugin(path: impl AsRef<Path>) {
}

pub struct Spec {
pub fs_limit: u64,
pub compile_limit: (Cpu, Memory, u64, Duration),
pub judge_limit: (Cpu, Memory, u64, Duration),
judge_limit: (Cpu, Memory, u64, Duration),
pub compile_command: Vec<OsString>,
pub judge_command: Vec<OsString>,
}

impl Spec {
pub fn get_judge_limit(&self, cpu: Cpu, mem: Memory) -> (Cpu, Memory, u64, Duration) {
todo!()
}
pub fn from_str(content: &str) -> Self {
let mut raw: Raw = toml::from_str(content).unwrap();
raw.compile.fill();
raw.judge.fill();

Self {
fs_limit: todo!(),
compile_limit: (
Cpu {
kernel: todo!(),
Expand Down
51 changes: 51 additions & 0 deletions judger/src/language/stage/assert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::sync::Arc;

use crate::{
language::spec::Spec,
sandbox::{Corpse, MonitorKind},
};

pub enum AssertionMode {
SkipSpace,
SkipContinousSpace,
Exact,
}

pub enum AssertResult {
Accept,
WrongAnswer,
RuntimeError,
TimeLimitExceeded,
MemoryLimitExceeded,
OutputLimitExceeded,
RealTimeLimitExceeded,
CompileError,
SystemError,
}
pub struct AssertRunner {
pub spec: Arc<Spec>,
pub corpse: Corpse,
}

impl AssertRunner {
pub fn new(spec: Arc<Spec>, corpse: Corpse) -> Self {
Self { spec, corpse }
}
fn assert_output(&self, output: &[u8], mode: AssertionMode) -> AssertResult {
todo!()
}
pub fn get_result(&self, output: &[u8], mode: AssertionMode) -> AssertResult {
match self.corpse.status() {
Ok(status) => match status.success() {
true => self.assert_output(output, mode),
false => AssertResult::WrongAnswer,
},
Err(reason) => match reason {
MonitorKind::Cpu => AssertResult::TimeLimitExceeded,
MonitorKind::Memory => AssertResult::MemoryLimitExceeded,
MonitorKind::Output => AssertResult::OutputLimitExceeded,
MonitorKind::Walltime => AssertResult::RealTimeLimitExceeded,
},
}
}
}
66 changes: 66 additions & 0 deletions judger/src/language/stage/compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::{path::PathBuf, sync::Arc, time::Duration};

use crate::{
filesystem::MountHandle,
language::spec::Spec,
sandbox::{Context, Limit, Process},
Result,
};

use super::JudgeRunner;

pub struct CompileRunner {
spec: Arc<Spec>,
handle: MountHandle,
}

impl CompileRunner {
pub fn new(spec: Arc<Spec>, handle: MountHandle) -> Self {
Self { spec, handle }
}
pub async fn run(self) -> Result<Option<JudgeRunner>> {
let ctx = CompileCtx {
spec: self.spec.clone(),
path: self.handle.get_path().to_path_buf(),
};
let process = Process::new(ctx)?;
let corpse = process.wait(Vec::new()).await?;
if !corpse.success() {
log::debug!("compile failed {:?}", corpse.status());
return Ok(None);
}

let runner = JudgeRunner::new(self.handle, self.spec);
Ok(Some(runner))
}
}

struct CompileCtx {
spec: Arc<Spec>,
path: PathBuf,
}

impl Limit for CompileCtx {
fn get_cpu(&mut self) -> crate::sandbox::Cpu {
todo!()
}
fn get_memory(&mut self) -> crate::sandbox::Memory {
todo!()
}
fn get_output(&mut self) -> u64 {
todo!()
}
fn get_walltime(&mut self) -> Duration {
todo!()
}
}

impl Context for CompileCtx {
type FS = PathBuf;
fn get_fs(&mut self) -> Self::FS {
self.path.clone()
}
fn get_args(&mut self) -> impl Iterator<Item = &std::ffi::OsStr> {
self.spec.compile_command.iter().map(|arg| arg.as_os_str())
}
}
63 changes: 63 additions & 0 deletions judger/src/language/stage/judge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::{path::PathBuf, sync::Arc, time::Duration};

use crate::{
filesystem::MountHandle,
language::spec::Spec,
sandbox::{Context, Cpu, Limit, Memory, Process},
Result,
};

use super::assert::AssertRunner;

pub struct JudgeRunner {
filesystem: MountHandle,
spec: Arc<Spec>,
}

impl JudgeRunner {
pub fn new(filesystem: MountHandle, spec: Arc<Spec>) -> Self {
Self { filesystem, spec }
}
pub async fn run(self, (mem, cpu): (Memory, Cpu), input: Vec<u8>) -> Result<AssertRunner> {
let ctx = JudgeCtx {
spec: self.spec.clone(),
path: self.filesystem.get_path().to_path_buf(),
limit: self.spec.get_judge_limit(cpu, mem),
};
let process = Process::new(ctx)?;
let corpse = process.wait(input).await?;
drop(self.filesystem);
Ok(AssertRunner::new(self.spec, corpse))
}
}

struct JudgeCtx {
spec: Arc<Spec>,
path: std::path::PathBuf,
limit: (Cpu, Memory, u64, Duration),
}

impl Limit for JudgeCtx {
fn get_cpu(&mut self) -> Cpu {
self.limit.0.clone()
}
fn get_memory(&mut self) -> Memory {
self.limit.1.clone()
}
fn get_output(&mut self) -> u64 {
self.limit.2
}
fn get_walltime(&mut self) -> Duration {
self.limit.3
}
}

impl Context for JudgeCtx {
type FS = PathBuf;
fn get_fs(&mut self) -> Self::FS {
self.path.clone()
}
fn get_args(&mut self) -> impl Iterator<Item = &std::ffi::OsStr> {
self.spec.judge_command.iter().map(|s| s.as_ref())
}
}
6 changes: 6 additions & 0 deletions judger/src/language/stage/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mod assert;
mod compile;
mod judge;

pub use compile::CompileRunner;
pub use judge::JudgeRunner;
2 changes: 1 addition & 1 deletion judger/src/sandbox/process/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl<C: Context> Process<C> {
tokio::spawn(async move { stdin.write_all(&input).await });

let stdout = process.stdout.take().unwrap();
let io_proxy=tokio::spawn(async move {
let io_proxy = tokio::spawn(async move {
let mut stdout = stdout;
if let Err(err) = io::copy(&mut stdout, &mut self.stdout).await {
log::debug!("Fail forwarding buffer: {}", err);
Expand Down

0 comments on commit fc7cf4c

Please sign in to comment.