Skip to content

Commit

Permalink
Merge pull request #132 from traP-jp/feat/#131-local-judge
Browse files Browse the repository at this point in the history
✨ fix many(implemented LocalJudge in traopy)
  • Loading branch information
comavius authored Feb 19, 2025
2 parents 6b3403f + a89aaef commit b3ecbde
Show file tree
Hide file tree
Showing 26 changed files with 618 additions and 584 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tonic = "0.12.3"
tonic-types = "0.12.3"
tonic-web = "0.12.3"
tonic-build = "0.12.3"
pyo3 = { version = "0.23.4", features = ["extension-module"] }
pyo3 = { version = "0.23.4", features = ["extension-module", "experimental-async"] }
pyo3-stub-gen = "0.7.0"


Expand Down
99 changes: 33 additions & 66 deletions lib/judge_core/src/job.rs
Original file line number Diff line number Diff line change
@@ -1,89 +1,56 @@
use crate::identifiers::ResourceId;
use futures::future::Future;
use std::process::Output;
use tokio::sync::broadcast;

/// JobAPI is a set of shell environment and cache of outcome files of previous jobs.
///
/// Instances must be initialized once per submission.
pub trait JobApi<JobOutcome: Clone>: Clone {
/// Greater the priority is, sooner the job will be executed.
///
/// Files created by this job will be deleted immediately after all returned JobOutcome are dropped.
///
/// Outer future only creates a kind of reservasion for shell environment and returns inner future synchronously.
fn run_future(
pub trait JobApi<ReservationToken, OutcomeToken: Clone>: Clone {
fn reserve_execution(
&self,
job_conf: ExecutionJob<JobOutcome>,
) -> impl Future<
Output = Result<
impl Future<Output = Result<ExecutionJobFinished<JobOutcome>, ExecutionJobError>>,
ExecutionJobPreparationError,
>,
>;
count: usize,
) -> impl Future<Output = Result<Vec<ReservationToken>, ReservationError>>;

fn place_file(
&self,
job_conf: FilePlacementJob,
) -> impl Future<Output = Result<JobOutcome, FilePlacementJobError>>;
}

#[derive(Debug, Clone)]
pub enum ExecutionJobFinished<JobOutcome: Clone> {
/// Job finished successfully.
Succeeded(JobOutcome, Output),
/// Job failed expectedly.
FailedExpectedly((JobOutcome, Output)),
/// Preceding job failed expectedly.
PrecedingJobFailedExpectedly,
}
file_conf: FileConf,
) -> impl Future<Output = Result<OutcomeToken, FilePlacementError>>;

#[derive(Debug, Clone)]
pub enum JobOutcomeAcquisitionResult<JobOutcome: Clone> {
/// Received JobOutcome successfully.
Succeeded(JobOutcome),
/// Failed to receive JobOutcome expectedly.
FailedExpectedly,
/// Failed to receive JobOutcome unexpectedly.
FailedUnexpectedly(String),
}

pub struct JobOutcomeLink<JobOutcome: Clone> {
pub job_outcome_rx: broadcast::Receiver<JobOutcomeAcquisitionResult<JobOutcome>>,
pub envvar_name: String,
fn execute(
&self,
reservation: ReservationToken,
dependencies: Vec<Dependency<OutcomeToken>>,
) -> impl Future<Output = Result<(OutcomeToken, Output), ExecutionError>>;
}

pub struct ExecutionJob<JobOutcome: Clone> {
pub script: String,
pub depends_on_with_names: Vec<JobOutcomeLink<JobOutcome>>,
#[derive(Debug, Clone, thiserror::Error)]
pub enum ReservationError {
#[error("Failed to reserve execution with error: {0}")]
ReserveFailed(String),
}

#[derive(Debug, Clone, thiserror::Error)]
pub enum ExecutionJobPreparationError {
#[error("Internal error while preparing a job: {0}")]
InternalError(String),
pub enum FilePlacementError {
#[error("Failed to place file with error: {0}")]
PlaceFailed(String),
#[error("Invalid resource ID: {0}")]
InvalidResourceId(ResourceId),
}

#[derive(Debug, Clone, thiserror::Error)]
pub enum ExecutionJobError {
#[error("Internal error while running a job: {0}")]
pub enum ExecutionError {
#[error("Internal error while executing a job: {0}")]
InternalError(String),
#[error("Preceding job failed unexpectedly: {0}")]
PrecedingJobFailed(String),
#[error("Judge process failed with error: {0}")]
JudgeFailed(String),
}

pub enum FilePlacementJob {
PlaceEmptyDirectory,
/// Content of the text file
PlaceRuntimeTextFile(String),
/// Global project-wide unique identifier
PlaceTextFile(ResourceId),
#[derive(Debug, Clone)]
pub enum FileConf {
EmptyDirectory,
Text(ResourceId),
RuntimeText(String),
}

#[derive(Debug, thiserror::Error)]
pub enum FilePlacementJobError {
#[error("Invalid resource id: {0}")]
InvalidResourceId(ResourceId),
#[error("Internal error while placing a file: {0}")]
InternalError(String),
#[derive(Debug, Clone)]
pub struct Dependency<OutcomeToken> {
pub envvar: String,
pub outcome: OutcomeToken,
}
33 changes: 19 additions & 14 deletions lib/judge_core/src/judge_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,58 @@ pub enum JudgeStatus {
CE,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ContinueStatus {
Continue,
Stop,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisplayableJudgeResult {
pub struct DisplayableExecutionResult {
pub status: JudgeStatus,
pub time: f64,
pub memory: f64,
pub score: i64,
pub message: Option<String>,
pub continue_status: ContinueStatus,
}

/// This returns from exec container as stdout
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum JudgeReport {
pub enum ExecutionResult {
/// Frontend-displayable execution result
Displayable(DisplayableJudgeResult),
Displayable(DisplayableExecutionResult),
/// Not displayed to frontend (e.g. for validation)
Hidden,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionReport {
pub result: ExecutionResult,
pub continue_status: ContinueStatus,
}

/// This is the final response from judge
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExecutionResponse {
Report(JudgeReport),
Report(ExecutionReport),
EarlyExit,
Error(String),
}

#[derive(Debug, Clone, thiserror::Error)]
pub enum JudgeOutputParseError {
pub enum ExecutionOutputParseError {
#[error("Invalid JSON: {0}")]
InvalidJson(String),
#[error("Non-zero exit code")]
NonZeroExitCode,
}

pub fn parse(output: &std::process::Output) -> Result<JudgeReport, JudgeOutputParseError> {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
pub fn parse(output: &std::process::Output) -> Result<ExecutionReport, ExecutionOutputParseError> {
let stdout = String::from_utf8(output.stdout.clone())
.map_err(|e| ExecutionOutputParseError::InvalidJson(e.to_string()))?;
if !output.status.success() {
return Err(JudgeOutputParseError::NonZeroExitCode);
return Err(ExecutionOutputParseError::NonZeroExitCode);
}
let judge_report: JudgeReport = serde_json::from_str(&stdout)
.map_err(|e| JudgeOutputParseError::InvalidJson(e.to_string()))?;
Ok(judge_report)
let execution_report: ExecutionReport = serde_json::from_str(&stdout)
.map_err(|e| ExecutionOutputParseError::InvalidJson(e.to_string()))?;
Ok(execution_report)
}
1 change: 1 addition & 0 deletions lib/judge_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ pub mod job;
pub mod judge_output;
pub mod problem_registry;
pub mod procedure;
pub mod registered_procedure_converter;
pub mod runner;
pub mod writer_schema_transpiler;
2 changes: 1 addition & 1 deletion lib/judge_core/src/problem_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub trait ProblemRegistryServer {
}

/// ProblemRegistryClient fetches contents of problems from the registry in judge server.
pub trait ProblemRegistryClient {
pub trait ProblemRegistryClient: Clone {
fn fetch(
&self,
resource_id: ResourceId,
Expand Down
7 changes: 6 additions & 1 deletion lib/judge_core/src/procedure/registered.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
use crate::identifiers::{DepId, ResourceId};

#[derive(Debug, Clone)]
pub struct Procedure {
pub runtime_texts: Vec<RuntimeText>,
pub texts: Vec<Text>,
pub empty_directories: Vec<EmptyDirectory>,
pub executions: Vec<Execution>,
}

#[derive(Debug, Clone)]
pub struct RuntimeText {
pub label: String,
pub dep_id: DepId,
}

#[derive(Debug, Clone)]
pub struct Text {
pub resource_id: ResourceId,
pub dep_id: DepId,
}

#[derive(Debug, Clone)]
pub struct EmptyDirectory {
pub dep_id: DepId,
}

#[derive(Debug, Clone)]
pub struct Execution {
pub script: String,
pub depends_on: Vec<DependsOn>,
pub dep_id: DepId,
}

#[derive(Debug, Clone)]
pub struct DependsOn {
pub dep_id: DepId,
pub envvar_name: String,
Expand Down
7 changes: 6 additions & 1 deletion lib/judge_core/src/procedure/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
use crate::identifiers::{ResourceId, RuntimeId};

#[derive(Debug, Clone)]
pub struct Procedure {
pub runtime_texts: Vec<RuntimeText>,
pub texts: Vec<Text>,
pub empty_directories: Vec<EmptyDirectory>,
pub executions: Vec<Execution>,
}

#[derive(Debug, Clone)]
pub struct RuntimeText {
pub content: String,
pub runtime_id: RuntimeId,
}

#[derive(Debug, Clone)]
pub struct Text {
pub resource_id: ResourceId,
pub runtime_id: RuntimeId,
}

#[derive(Debug, Clone)]
pub struct EmptyDirectory {
pub runtime_id: RuntimeId,
}

#[derive(Debug, Clone)]
pub struct Execution {
pub script: String,
pub depends_on: Vec<DependsOn>,
pub runtime_id: RuntimeId,
}

#[derive(Debug, Clone)]
pub struct DependsOn {
pub runtime_id: RuntimeId,
pub envvar_name: String,
Expand Down
8 changes: 1 addition & 7 deletions lib/judge_core/src/procedure/writer_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
pub struct Procedure {
pub resources: Vec<ResourceKind>,
pub executions: Vec<Execution>,
pub scripts: Vec<Script>,
pub scripts: Vec<Text>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -38,12 +38,6 @@ pub struct RuntimeText {
pub label: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Script {
pub content: String,
pub name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Text {
pub name: String,
Expand Down
Loading

0 comments on commit b3ecbde

Please sign in to comment.