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
|
||||
.vscode
|
||||
.devcontainer
|
||||
|
||||
@ -15,6 +15,8 @@ async-stream = "0.2"
|
||||
tower = "0.3"
|
||||
tempfile = "3"
|
||||
libc = "0.2.76"
|
||||
log = "0.4.0"
|
||||
env_logger = "0.8.1"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build ={ version = "0.3" }
|
||||
|
||||
22
README.md
22
README.md
@ -3,3 +3,25 @@
|
||||
## 环境要求
|
||||
|
||||
- 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 {
|
||||
int64 time_used = 1;
|
||||
int64 memory_used = 2;
|
||||
JudgeResult result = 3;
|
||||
oneof state {
|
||||
JudgeResult result = 3;
|
||||
JudgeStatus status = 9;
|
||||
}
|
||||
// int32 errno = 4;
|
||||
// int32 exit_code = 5;
|
||||
// string stdout = 6;
|
||||
// string stderr = 7;
|
||||
string errmsg = 8;
|
||||
JudgeStatus status = 9;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::river::judge_response::State;
|
||||
use crate::river::JudgeResponse;
|
||||
use crate::river::{JudgeResult, JudgeStatus};
|
||||
use crate::river::JudgeResult;
|
||||
use libc::strerror;
|
||||
use std::ffi::{CStr, NulError, OsString};
|
||||
use std::fmt;
|
||||
@ -21,6 +22,7 @@ pub enum Error {
|
||||
UnknownRequestData,
|
||||
RequestDataNotFound,
|
||||
SyscallError(String),
|
||||
CustomError(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
@ -44,6 +46,7 @@ impl fmt::Display for Error {
|
||||
let reason = errno_str(errno);
|
||||
write!(f, "SyscallError: `{}` {}", syscall, reason)
|
||||
}
|
||||
Error::CustomError(ref reason) => write!(f, "{}", reason),
|
||||
_ => write!(f, "{:?}", self),
|
||||
}
|
||||
}
|
||||
@ -53,8 +56,7 @@ pub fn system_error(err: Error) -> JudgeResponse {
|
||||
JudgeResponse {
|
||||
time_used: 0,
|
||||
memory_used: 0,
|
||||
result: JudgeResult::SystemError as i32,
|
||||
state: Some(State::Result(JudgeResult::SystemError as i32)),
|
||||
errmsg: format!("{}", err).into(),
|
||||
status: JudgeStatus::Ended as i32,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use super::error::{Error, Result};
|
||||
use super::process::Process;
|
||||
use super::runner::RunnerStatus;
|
||||
use crate::river::judge_response::State;
|
||||
use crate::river::Language;
|
||||
use crate::river::{CompileData, JudgeData, JudgeResult, JudgeStatus};
|
||||
use crate::river::{JudgeRequest, JudgeResponse};
|
||||
@ -12,9 +13,8 @@ impl JudgeResponse {
|
||||
JudgeResponse {
|
||||
time_used: 0,
|
||||
memory_used: 0,
|
||||
result: JudgeResult::Accepted as i32,
|
||||
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];
|
||||
reader.readline(&mut buf)?;
|
||||
|
||||
resp.status = JudgeStatus::Ended as i32;
|
||||
// TODO: 根据返回值等判断 tle、mle、re 等状态
|
||||
// TODO: 对比答案,检查结果
|
||||
if status.exit_code != 0 {
|
||||
resp.set_process_status(&status);
|
||||
resp.result = JudgeResult::WrongAnswer as i32;
|
||||
resp.status = JudgeStatus::Ended as i32;
|
||||
resp.state = Some(State::Result(JudgeResult::WrongAnswer as i32));
|
||||
}
|
||||
Ok(resp)
|
||||
}
|
||||
@ -79,7 +77,6 @@ pub async fn compile(
|
||||
path: &Path,
|
||||
) -> Result<JudgeResponse> {
|
||||
let mut resp = JudgeResponse::new();
|
||||
resp.status = JudgeStatus::Ended as i32;
|
||||
// 写入代码
|
||||
let filename = match Language::from_i32(request.language) {
|
||||
Some(Language::C) => "main.c",
|
||||
@ -112,17 +109,26 @@ pub async fn compile(
|
||||
|
||||
let runner = process.runner.clone();
|
||||
let status = runner.await?;
|
||||
resp.set_process_status(&status);
|
||||
if status.exit_code != 0 {
|
||||
resp.set_process_status(&status);
|
||||
// resp.errmsg = format!("{}{}", status.stdout, status.stderr);
|
||||
resp.result = JudgeResult::CompileError as i32;
|
||||
debug!("compile exit code: {}", status.exit_code);
|
||||
let reader = process.stdout_reader()?;
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn pending() -> JudgeResponse {
|
||||
let mut resp = JudgeResponse::new();
|
||||
resp.status = JudgeStatus::Pending as i32;
|
||||
resp.state = Some(State::Status(JudgeStatus::Pending as i32));
|
||||
resp
|
||||
}
|
||||
|
||||
|
||||
23
src/main.rs
23
src/main.rs
@ -1,5 +1,8 @@
|
||||
#![recursion_limit = "256"]
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use env_logger::Env;
|
||||
use futures::StreamExt;
|
||||
use futures_core::Stream;
|
||||
use river::judge_request::Data;
|
||||
@ -36,7 +39,6 @@ impl River for RiverService {
|
||||
let mut stream = request.into_inner();
|
||||
|
||||
let output = async_stream::try_stream! {
|
||||
|
||||
// 创建评测使用的临时目录
|
||||
// 无需主动删除,变量在 drop 之后会自动删除临时文件夹
|
||||
let pwd = match tempdir_in("./runner") {
|
||||
@ -46,6 +48,7 @@ impl River for RiverService {
|
||||
return
|
||||
},
|
||||
};
|
||||
debug!("create tempdir: {:?}", pwd);
|
||||
|
||||
while let Some(req) = stream.next().await {
|
||||
// TODO: 使用锁或者资源量等机制限制并发
|
||||
@ -55,8 +58,14 @@ impl River for RiverService {
|
||||
|
||||
yield judger::running();
|
||||
let result = match &req.data {
|
||||
Some(Data::CompileData(data)) => judger::compile(&req, &data, &pwd.path()).await,
|
||||
Some(Data::JudgeData(data)) => judger::judger(&req, &data, &pwd.path()).await,
|
||||
Some(Data::CompileData(data)) => {
|
||||
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),
|
||||
_ => Err(error::Error::UnknownRequestData),
|
||||
};
|
||||
@ -75,9 +84,17 @@ impl River for RiverService {
|
||||
|
||||
#[tokio::main]
|
||||
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 river = RiverService::default();
|
||||
|
||||
info!("listen on: {}", addr);
|
||||
|
||||
Server::builder()
|
||||
.concurrency_limit_per_connection(5)
|
||||
.add_service(RiverServer::new(river))
|
||||
|
||||
@ -78,6 +78,9 @@ impl Process {
|
||||
|
||||
// 为进程设置 stdin 的数据
|
||||
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 = format!("{}{}", "stdin", memfile);
|
||||
// 打开内存文件
|
||||
|
||||
@ -2,8 +2,10 @@ use super::error::{Error, Result};
|
||||
use libc;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
use std::str;
|
||||
|
||||
pub struct Reader {
|
||||
fd: i32,
|
||||
stream: *mut libc::FILE,
|
||||
}
|
||||
|
||||
@ -14,7 +16,7 @@ impl Reader {
|
||||
Err(e) => return Err(Error::StringToCStringError(e)),
|
||||
};
|
||||
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<()> {
|
||||
let res = unsafe {
|
||||
@ -29,6 +31,19 @@ impl Reader {
|
||||
}
|
||||
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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user