This commit is contained in:
MeiK 2020-10-22 10:01:18 +00:00
parent d1d74d2b6c
commit 8ba1eb2d90
9 changed files with 89 additions and 19 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ Cargo.lock
.idea .idea
.vscode .vscode
.devcontainer

View File

@ -15,6 +15,8 @@ async-stream = "0.2"
tower = "0.3" tower = "0.3"
tempfile = "3" tempfile = "3"
libc = "0.2.76" libc = "0.2.76"
log = "0.4.0"
env_logger = "0.8.1"
[build-dependencies] [build-dependencies]
tonic-build ={ version = "0.3" } tonic-build ={ version = "0.3" }

View File

@ -3,3 +3,25 @@
## 环境要求 ## 环境要求
- linux - linux
## Example
```json
{
"language": 0,
"judge_type": 0,
"compile_data": {
"code": "I2luY2x1ZGUgPHN0ZGlvLmg+CmludCBtYWluKCkgewogIHByaW50ZigiSGVsbG8gV29ybGQhXG4iKTsKICByZXR1cm4gMDsKfQ=="
},
"judge_data": {
"in_data": "SGVsbG8gV29ybGQh",
"out_data": "SGVsbG8gV29ybGQh",
"time_limit": 1000,
"memory_limit": 65535
}
}
```
## TODOs
- 修改为文件形式的输入输出,为了这部分的优化我浪费了太多的时间,最后发现过早的优化果然是万恶之源。

View File

@ -62,11 +62,13 @@ enum JudgeStatus {
message JudgeResponse { message JudgeResponse {
int64 time_used = 1; int64 time_used = 1;
int64 memory_used = 2; int64 memory_used = 2;
JudgeResult result = 3; oneof state {
JudgeResult result = 3;
JudgeStatus status = 9;
}
// int32 errno = 4; // int32 errno = 4;
// int32 exit_code = 5; // int32 exit_code = 5;
// string stdout = 6; // string stdout = 6;
// string stderr = 7; // string stderr = 7;
string errmsg = 8; string errmsg = 8;
JudgeStatus status = 9;
} }

View File

@ -1,5 +1,6 @@
use crate::river::judge_response::State;
use crate::river::JudgeResponse; use crate::river::JudgeResponse;
use crate::river::{JudgeResult, JudgeStatus}; use crate::river::JudgeResult;
use libc::strerror; use libc::strerror;
use std::ffi::{CStr, NulError, OsString}; use std::ffi::{CStr, NulError, OsString};
use std::fmt; use std::fmt;
@ -21,6 +22,7 @@ pub enum Error {
UnknownRequestData, UnknownRequestData,
RequestDataNotFound, RequestDataNotFound,
SyscallError(String), SyscallError(String),
CustomError(String),
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
@ -44,6 +46,7 @@ impl fmt::Display for Error {
let reason = errno_str(errno); let reason = errno_str(errno);
write!(f, "SyscallError: `{}` {}", syscall, reason) write!(f, "SyscallError: `{}` {}", syscall, reason)
} }
Error::CustomError(ref reason) => write!(f, "{}", reason),
_ => write!(f, "{:?}", self), _ => write!(f, "{:?}", self),
} }
} }
@ -53,8 +56,7 @@ pub fn system_error(err: Error) -> JudgeResponse {
JudgeResponse { JudgeResponse {
time_used: 0, time_used: 0,
memory_used: 0, memory_used: 0,
result: JudgeResult::SystemError as i32, state: Some(State::Result(JudgeResult::SystemError as i32)),
errmsg: format!("{}", err).into(), errmsg: format!("{}", err).into(),
status: JudgeStatus::Ended as i32,
} }
} }

View File

@ -1,6 +1,7 @@
use super::error::{Error, Result}; use super::error::{Error, Result};
use super::process::Process; use super::process::Process;
use super::runner::RunnerStatus; use super::runner::RunnerStatus;
use crate::river::judge_response::State;
use crate::river::Language; use crate::river::Language;
use crate::river::{CompileData, JudgeData, JudgeResult, JudgeStatus}; use crate::river::{CompileData, JudgeData, JudgeResult, JudgeStatus};
use crate::river::{JudgeRequest, JudgeResponse}; use crate::river::{JudgeRequest, JudgeResponse};
@ -12,9 +13,8 @@ impl JudgeResponse {
JudgeResponse { JudgeResponse {
time_used: 0, time_used: 0,
memory_used: 0, memory_used: 0,
result: JudgeResult::Accepted as i32,
errmsg: "".into(), errmsg: "".into(),
status: JudgeStatus::Running as i32, state: Some(State::Status(JudgeStatus::Running as i32)),
} }
} }
@ -62,13 +62,11 @@ pub async fn judger(
let mut buf: [u8; 1024] = [0; 1024]; let mut buf: [u8; 1024] = [0; 1024];
reader.readline(&mut buf)?; reader.readline(&mut buf)?;
resp.status = JudgeStatus::Ended as i32;
// TODO: 根据返回值等判断 tle、mle、re 等状态 // TODO: 根据返回值等判断 tle、mle、re 等状态
// TODO: 对比答案,检查结果 // TODO: 对比答案,检查结果
if status.exit_code != 0 { if status.exit_code != 0 {
resp.set_process_status(&status); resp.set_process_status(&status);
resp.result = JudgeResult::WrongAnswer as i32; resp.state = Some(State::Result(JudgeResult::WrongAnswer as i32));
resp.status = JudgeStatus::Ended as i32;
} }
Ok(resp) Ok(resp)
} }
@ -79,7 +77,6 @@ pub async fn compile(
path: &Path, path: &Path,
) -> Result<JudgeResponse> { ) -> Result<JudgeResponse> {
let mut resp = JudgeResponse::new(); let mut resp = JudgeResponse::new();
resp.status = JudgeStatus::Ended as i32;
// 写入代码 // 写入代码
let filename = match Language::from_i32(request.language) { let filename = match Language::from_i32(request.language) {
Some(Language::C) => "main.c", Some(Language::C) => "main.c",
@ -112,17 +109,26 @@ pub async fn compile(
let runner = process.runner.clone(); let runner = process.runner.clone();
let status = runner.await?; let status = runner.await?;
resp.set_process_status(&status);
if status.exit_code != 0 { if status.exit_code != 0 {
resp.set_process_status(&status); debug!("compile exit code: {}", status.exit_code);
// resp.errmsg = format!("{}{}", status.stdout, status.stderr); let reader = process.stdout_reader()?;
resp.result = JudgeResult::CompileError as i32; let stdout = reader.read()?;
debug!("stdout {}", stdout);
let reader = process.stderr_reader()?;
let stderr = reader.read()?;
debug!("stderr {}", stderr);
resp.errmsg = stderr;
resp.state = Some(State::Result(JudgeResult::CompileError as i32));
} else {
resp.state = Some(State::Result(JudgeResult::Accepted as i32));
} }
Ok(resp) Ok(resp)
} }
pub fn pending() -> JudgeResponse { pub fn pending() -> JudgeResponse {
let mut resp = JudgeResponse::new(); let mut resp = JudgeResponse::new();
resp.status = JudgeStatus::Pending as i32; resp.state = Some(State::Status(JudgeStatus::Pending as i32));
resp resp
} }

View File

@ -1,5 +1,8 @@
#![recursion_limit = "256"] #![recursion_limit = "256"]
#[macro_use]
extern crate log;
use env_logger::Env;
use futures::StreamExt; use futures::StreamExt;
use futures_core::Stream; use futures_core::Stream;
use river::judge_request::Data; use river::judge_request::Data;
@ -36,7 +39,6 @@ impl River for RiverService {
let mut stream = request.into_inner(); let mut stream = request.into_inner();
let output = async_stream::try_stream! { let output = async_stream::try_stream! {
// 创建评测使用的临时目录 // 创建评测使用的临时目录
// 无需主动删除,变量在 drop 之后会自动删除临时文件夹 // 无需主动删除,变量在 drop 之后会自动删除临时文件夹
let pwd = match tempdir_in("./runner") { let pwd = match tempdir_in("./runner") {
@ -46,6 +48,7 @@ impl River for RiverService {
return return
}, },
}; };
debug!("create tempdir: {:?}", pwd);
while let Some(req) = stream.next().await { while let Some(req) = stream.next().await {
// TODO: 使用锁或者资源量等机制限制并发 // TODO: 使用锁或者资源量等机制限制并发
@ -55,8 +58,14 @@ impl River for RiverService {
yield judger::running(); yield judger::running();
let result = match &req.data { let result = match &req.data {
Some(Data::CompileData(data)) => judger::compile(&req, &data, &pwd.path()).await, Some(Data::CompileData(data)) => {
Some(Data::JudgeData(data)) => judger::judger(&req, &data, &pwd.path()).await, debug!("compile request");
judger::compile(&req, &data, &pwd.path()).await
},
Some(Data::JudgeData(data)) => {
debug!("judge request");
judger::judger(&req, &data, &pwd.path()).await
},
None => Err(error::Error::RequestDataNotFound), None => Err(error::Error::RequestDataNotFound),
_ => Err(error::Error::UnknownRequestData), _ => Err(error::Error::UnknownRequestData),
}; };
@ -75,9 +84,17 @@ impl River for RiverService {
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let env = Env::default()
.filter_or("MY_LOG_LEVEL", "debug")
.write_style_or("MY_LOG_STYLE", "always");
env_logger::init_from_env(env);
let addr = "127.0.0.1:4003".parse()?; let addr = "127.0.0.1:4003".parse()?;
let river = RiverService::default(); let river = RiverService::default();
info!("listen on: {}", addr);
Server::builder() Server::builder()
.concurrency_limit_per_connection(5) .concurrency_limit_per_connection(5)
.add_service(RiverServer::new(river)) .add_service(RiverServer::new(river))

View File

@ -78,6 +78,9 @@ impl Process {
// 为进程设置 stdin 的数据 // 为进程设置 stdin 的数据
pub fn set_stdin(&mut self, in_data: &Vec<u8>) -> Result<()> { pub fn set_stdin(&mut self, in_data: &Vec<u8>) -> Result<()> {
if in_data.len() == 0 {
return Ok(())
}
let memfile = path_buf_str(&self.runner.workdir)?; let memfile = path_buf_str(&self.runner.workdir)?;
let memfile = format!("{}{}", "stdin", memfile); let memfile = format!("{}{}", "stdin", memfile);
// 打开内存文件 // 打开内存文件

View File

@ -2,8 +2,10 @@ use super::error::{Error, Result};
use libc; use libc;
use std::ffi::CString; use std::ffi::CString;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::str;
pub struct Reader { pub struct Reader {
fd: i32,
stream: *mut libc::FILE, stream: *mut libc::FILE,
} }
@ -14,7 +16,7 @@ impl Reader {
Err(e) => return Err(Error::StringToCStringError(e)), Err(e) => return Err(Error::StringToCStringError(e)),
}; };
let stream = unsafe { libc::fdopen(fd, mode.as_ptr()) }; let stream = unsafe { libc::fdopen(fd, mode.as_ptr()) };
Ok(Reader { stream: stream }) Ok(Reader { fd, stream: stream })
} }
pub fn readline(&self, buf: &mut [u8]) -> Result<()> { pub fn readline(&self, buf: &mut [u8]) -> Result<()> {
let res = unsafe { let res = unsafe {
@ -29,6 +31,19 @@ impl Reader {
} }
Ok(()) Ok(())
} }
pub fn read(&self) -> Result<String> {
let mut result: String = "".to_owned();
let mut buf: [u8; 1024] = [0; 1024];
while unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, 1024) } != 0 {
let s = match str::from_utf8(&buf) {
Ok(val) => (val),
Err(_) => return Err(Error::SyscallError("read".to_string())),
};
debug!("{}", s);
result.push_str(s);
}
Ok(result)
}
} }
impl Drop for Reader { impl Drop for Reader {