From 32aec35ff7da75f0f23131f2089fa51682f23fee Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Fri, 2 Oct 2026 13:25:42 +0700 Subject: [PATCH 1/3] fix(judge): run both write to stdin and read from stdout concurrently --- src/judge.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/judge.rs b/src/judge.rs index 59e572a..3ce8223 100644 --- a/src/judge.rs +++ b/src/judge.rs @@ -118,7 +118,7 @@ impl Judge { } #[require(Compiled)] - pub async fn run(&self, input: &[u8]) -> io::Result { + pub async fn run(&self, input: Vec) -> io::Result { let checker_language = self .checker_language .ok_or(io::Error::other("Missing checker"))?; @@ -131,7 +131,7 @@ impl Judge { .spawn()?; let mut cstdin = checker.stdin.take().unwrap(); let mut cstdout = checker.stdout.take().unwrap(); - cstdin.write_all(input).await?; + cstdin.write_all(&input).await?; cstdin.write_all(b"\n").await?; cstdin.flush().await?; @@ -147,13 +147,15 @@ impl Judge { let mut stderr = main.stderr.take().unwrap(); let monitor = tokio::spawn(async move { sandbox.monitor(main).await }); - if !self.is_interactive { - stdin.write_all(input).await?; - stdin.write_all(b"\n").await?; - stdin.flush().await?; - } - let stdin_thread = - tokio::spawn(async move { tokio::io::copy(&mut cstdout, &mut stdin).await }); + let is_interactive = self.is_interactive; + let stdin_thread = tokio::spawn(async move { + if !is_interactive { + stdin.write_all(&input).await?; + stdin.write_all(b"\n").await?; + stdin.flush().await?; + } + tokio::io::copy(&mut cstdout, &mut stdin).await + }); let stdout_thread = tokio::spawn(async move { let mut out = vec![]; let mut buffer = [0u8; BUFFER_SIZE]; @@ -209,14 +211,13 @@ impl Judge { #[require(Compiled)] pub async fn batch_run( &self, - inputs: impl Iterator, + inputs: impl Iterator>, ) -> io::Result { let mut verdict = Verdict::Accepted; let mut total_run_time = Duration::ZERO; let mut total_memory_usage = Byte::default(); let mut count = 0; - // running sequentially to enable early exit, saving resources for input in inputs { let metrics = self.run(input).await?; total_run_time += metrics.run_time; @@ -242,14 +243,13 @@ impl Judge { #[require(Compiled)] pub async fn streamed_batch_run( &self, - mut inputs: impl Stream + std::marker::Unpin, + mut inputs: impl Stream> + std::marker::Unpin, ) -> io::Result { let mut verdict = Verdict::Accepted; let mut total_run_time = Duration::ZERO; let mut total_memory_usage = Byte::default(); let mut count = 0; - // running sequentially to enable early exit, saving resources while let Some(input) = inputs.next().await { let metrics = self.run(input).await?; total_run_time += metrics.run_time; From f86c0f7ff51b5273ba927b7465c54b6fa3425987 Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Fri, 2 Oct 2026 13:28:14 +0700 Subject: [PATCH 2/3] refactor: remove batch run --- Cargo.lock | 1 - Cargo.toml | 3 +- src/judge.rs | 68 +-------------------------------- src/metrics.rs | 8 ---- tests/should_return_accepted.rs | 2 +- 5 files changed, 3 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5812ae2..af9cb92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,7 +358,6 @@ dependencies = [ "bon", "byte-unit", "cgroups-rs", - "futures-lite", "rand 0.10.1", "rstest", "serde", diff --git a/Cargo.toml b/Cargo.toml index d12777d..a3861fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ A code runner library for online judge system bon = "3.8.1" byte-unit = "5.2.0" cgroups-rs = "0.5.0" -futures-lite = "2.6.1" serde = { version = "1.0.228", features = ["derive"], optional = true } state-shift = "2.1.1" tokio = { version = "1.48.0", features = ["full"] } @@ -22,7 +21,7 @@ uuid = { version = "1.19.0", features = ["v4", "fast-rng"] } [dev-dependencies] rand = "0.10.1" -rstest = "0.26.1" +rstest = "0.27.0" [features] serde = ["dep:serde", "byte-unit/serde"] diff --git a/src/judge.rs b/src/judge.rs index 3ce8223..70da7a6 100644 --- a/src/judge.rs +++ b/src/judge.rs @@ -1,8 +1,6 @@ use std::{env, io, marker::PhantomData, path::PathBuf, process::Stdio, time::Duration}; use bon::bon; -use byte_unit::Byte; -use futures_lite::{Stream, StreamExt}; use state_shift::{impl_state, type_state}; use tokio::{ fs, @@ -10,7 +8,7 @@ use tokio::{ }; use uuid::Uuid; -use crate::{AggregatedMetrics, Language, Metrics, Resource, Sandbox, Verdict}; +use crate::{Language, Metrics, Resource, Sandbox, Verdict}; const MAIN: &str = "main"; const CHECKER: &str = "checker"; @@ -207,68 +205,4 @@ impl Judge { memory_usage, }) } - - #[require(Compiled)] - pub async fn batch_run( - &self, - inputs: impl Iterator>, - ) -> io::Result { - let mut verdict = Verdict::Accepted; - let mut total_run_time = Duration::ZERO; - let mut total_memory_usage = Byte::default(); - let mut count = 0; - - for input in inputs { - let metrics = self.run(input).await?; - total_run_time += metrics.run_time; - total_memory_usage = total_memory_usage - .add(metrics.memory_usage) - .expect("memory usage should not overflow u32"); - count += 1; - if metrics.verdict != Verdict::Accepted { - verdict = metrics.verdict; - break; - } - } - - Ok(AggregatedMetrics { - verdict, - average_run_time: total_run_time / count, - average_memory_usage: total_memory_usage - .divide(count as usize) - .expect("count must be greater than 0"), - }) - } - - #[require(Compiled)] - pub async fn streamed_batch_run( - &self, - mut inputs: impl Stream> + std::marker::Unpin, - ) -> io::Result { - let mut verdict = Verdict::Accepted; - let mut total_run_time = Duration::ZERO; - let mut total_memory_usage = Byte::default(); - let mut count = 0; - - while let Some(input) = inputs.next().await { - let metrics = self.run(input).await?; - total_run_time += metrics.run_time; - total_memory_usage = total_memory_usage - .add(metrics.memory_usage) - .expect("memory usage should not overflow u32"); - count += 1; - if metrics.verdict != Verdict::Accepted { - verdict = metrics.verdict; - break; - } - } - - Ok(AggregatedMetrics { - verdict, - average_run_time: total_run_time / count, - average_memory_usage: total_memory_usage - .divide(count as usize) - .expect("count must be greater than 0"), - }) - } } diff --git a/src/metrics.rs b/src/metrics.rs index 3689706..bc74c87 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -22,11 +22,3 @@ pub struct Metrics { pub stdout: Vec, pub stderr: Vec, } - -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -pub struct AggregatedMetrics { - pub verdict: Verdict, - pub average_run_time: Duration, - pub average_memory_usage: Byte, -} diff --git a/tests/should_return_accepted.rs b/tests/should_return_accepted.rs index c96c3f4..5d95dd3 100644 --- a/tests/should_return_accepted.rs +++ b/tests/should_return_accepted.rs @@ -44,7 +44,7 @@ pub async fn should_return_accepted( let judge = judge.compile().await.unwrap().unwrap(); for input in inputs { - let metrics = judge.run(input.as_bytes()).await.unwrap(); + let metrics = judge.run(input.into_bytes()).await.unwrap(); assert_eq!(metrics.verdict, Verdict::Accepted); } } From 9879b03adcded76ca9932341cc4d681962b57f6f Mon Sep 17 00:00:00 2001 From: akagiyuu Date: Fri, 2 Oct 2026 13:40:18 +0700 Subject: [PATCH 3/3] refactor: remove unused features --- Cargo.lock | 22 ++++------------------ Cargo.toml | 4 ---- examples/basic.rs | 2 +- src/metrics.rs | 1 - 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af9cb92..0a869fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,6 @@ dependencies = [ "cgroups-rs", "rand 0.10.1", "rstest", - "serde", "state-shift", "tokio", "uuid", @@ -544,17 +543,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "futures-task" version = "0.3.31" @@ -574,11 +562,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", - "futures-macro", "futures-task", "pin-project-lite", "pin-utils", - "slab", ] [[package]] @@ -1145,9 +1131,9 @@ dependencies = [ [[package]] name = "rstest" -version = "0.26.1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +checksum = "948203e6d13b83e51a90d7cf236dd58a0065f23f4b902f00f306e7eb2bd09b9c" dependencies = [ "futures-timer", "futures-util", @@ -1156,9 +1142,9 @@ dependencies = [ [[package]] name = "rstest_macros" -version = "0.26.1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +checksum = "a8d92eaf7b6e51e12471d71ddc72608e7151573de44099aacfbb1128177c0da4" dependencies = [ "cfg-if", "glob", diff --git a/Cargo.toml b/Cargo.toml index a3861fb..6a9509f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ A code runner library for online judge system bon = "3.8.1" byte-unit = "5.2.0" cgroups-rs = "0.5.0" -serde = { version = "1.0.228", features = ["derive"], optional = true } state-shift = "2.1.1" tokio = { version = "1.48.0", features = ["full"] } uuid = { version = "1.19.0", features = ["v4", "fast-rng"] } @@ -22,6 +21,3 @@ uuid = { version = "1.19.0", features = ["v4", "fast-rng"] } [dev-dependencies] rand = "0.10.1" rstest = "0.27.0" - -[features] -serde = ["dep:serde", "byte-unit/serde"] diff --git a/examples/basic.rs b/examples/basic.rs index 13e41ee..cb10ab5 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -63,6 +63,6 @@ int main() { let judge = judge.compile().await.unwrap().unwrap(); let input = "4"; - let metrics = judge.run(input.as_bytes()).await.unwrap(); + let metrics = judge.run(input.as_bytes().to_vec()).await.unwrap(); println!("{:#?}", metrics); } diff --git a/src/metrics.rs b/src/metrics.rs index bc74c87..8e0beda 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -3,7 +3,6 @@ use std::time::Duration; use byte_unit::Byte; #[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum Verdict { Accepted, WrongAnswer,