mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
Update
This commit is contained in:
parent
d1d74d2b6c
commit
8ba1eb2d90
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ Cargo.lock
|
|||||||
|
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
.devcontainer
|
||||||
|
|||||||
@ -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" }
|
||||||
|
|||||||
22
README.md
22
README.md
@ -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
|
||||||
|
|
||||||
|
- 修改为文件形式的输入输出,为了这部分的优化我浪费了太多的时间,最后发现过早的优化果然是万恶之源。
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/main.rs
23
src/main.rs
@ -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))
|
||||||
|
|||||||
@ -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);
|
||||||
// 打开内存文件
|
// 打开内存文件
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user