Skip to content

ThinkSpiritLab/Heng-Client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Jun 20, 2022
6699160 · Jun 20, 2022
Oct 28, 2021
Sep 13, 2021
May 23, 2022
Jun 20, 2022
May 23, 2022
May 23, 2022
May 23, 2022
May 23, 2022
May 23, 2022
May 23, 2022
Dec 16, 2020
May 23, 2022
Nov 16, 2021
Jun 20, 2022
Jun 20, 2022
May 23, 2022
May 23, 2022
May 23, 2022
Mar 2, 2021
Mar 3, 2021
May 24, 2022

Repository files navigation

众衡

本系统是为 ThinkSpirit 实验室的下一代在线评测系统设计的评测机系统的评测端。专职负责运行用户程序并得出判定结论。

系统的详细结构参见协议仓库, 参考的控制端实现在 Heng-Controler

本仓库的 Docker 镜像(语言环境已封装)见 https://hub.docker.com/r/thinkspiritlab/heng-client

部署

推荐关闭宿主机的 Swap。

Docker

Host的内核版本需大于 4.6 (支持cgroup_namespaces)

dnf install --assumeyes yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
dnf install --assumeyes docker-ce
systemctl start docker
systemctl enable docker
docker pull thinkspiritlab/heng-client:latest
docker run --cgroupns private --privileged -it -v $(pwd)/config.toml:/hc/config/config.toml thinkspiritlab/heng-client
# docker run --cgroupns private --privileged -d --restart=always -v $(pwd)/config.toml:/hc/config/config.toml thinkspiritlab/heng-client

CentOS8

cd ~
dnf install git -y
git clone https://github.com/ThinkSpiritLab/Heng-Client.git
cd ./Heng-Client
bash prepare-centos8.sh
cp config/config.example.toml config/config.toml
npm run start # pm2 start ./dist/index.js --name judger

Ubuntu20.04

sudo -i
cd ~
apt update
apt install git -y
git clone https://github.com/ThinkSpiritLab/Heng-Client.git
cd ./Heng-Client
bash prepare-ubuntu20.sh
cp config/config.example.toml config/config.toml
npm run start # pm2 start ./dist/index.js --name judger

依赖

沙盒部分

本系统依赖 nsjailHeng-Core 作为沙盒内核。需要在配置文件中指定对应路径。

它的仓库是 https://github.com/google/nsjailhttps://github.com/ThinkSpiritLab/Heng-Core

语言

为了评测机正常工作,需要其支持的各种语言环境。

运行时

开发使用 nodejs:14 不保证在更低版本下能够运行。

架构

基础设施

沙盒运行支持

使用 nsjailHeng-Core

语言支持

对于每种语言,实现一个 Language 类型的函数并在 src/Spawn/Language/index.tsimport 并注册。

限流

Throttle 模块实现了异步限流,调用方法是将逻辑封装在异步函数中发送给 withThrottle 方法。

业务逻辑

控制端模块

src/controller.ts 中实现了控制端相关逻辑。

调用时,实例化一个 Controller 对象,注册各类评测机方法后获取 token 并开启连接。

评测

仅通过 ExecutableAgent 调用外部程序,ExecutableAgent 有编译和运行功能;ExecutableAgent 传入可执行对象 Executable(包括代码、语言、限制),根据不同语言调用相应 Language。编译或运行时可提供输入输出流,ExecutableAgent 询问 Language 编译参数或运行参数,并作一些合并,然后执行。

对于 NormalSpj,用户程序的输出被重定向到文件,随后被提供给结果判断程序;对于 Interactiveuserinteractor 同时运行,双方的输入输出被 pipe 到另一方,由于管道缓冲区有容量限制,写满后写程序阻塞,要避免大量输入输出、避免 interactor 过慢、避免 interactor 时限小于 user

对于 NormalSpj,用户程序没有正常结束运行时,跳过执行结果判断程序。

其他

spj 返回值及输出结果汇总

#ifndef OK_EXIT_CODE
#   ifdef CONTESTER
#       define OK_EXIT_CODE 0xAC
#   else
#       define OK_EXIT_CODE 0
#   endif
#endif

#ifndef WA_EXIT_CODE
#   ifdef EJUDGE
#       define WA_EXIT_CODE 5
#   elif defined(CONTESTER)
#       define WA_EXIT_CODE 0xAB
#   else
#       define WA_EXIT_CODE 1
#   endif
#endif

#ifndef PE_EXIT_CODE
#   ifdef EJUDGE
#       define PE_EXIT_CODE 4
#   elif defined(CONTESTER)
#       define PE_EXIT_CODE 0xAA
#   else
#       define PE_EXIT_CODE 2
#   endif
#endif

#ifndef FAIL_EXIT_CODE
#   ifdef EJUDGE
#       define FAIL_EXIT_CODE 6
#   elif defined(CONTESTER)
#       define FAIL_EXIT_CODE 0xA3
#   else
#       define FAIL_EXIT_CODE 3
#   endif
#endif

#ifndef DIRT_EXIT_CODE
#   ifdef EJUDGE
#       define DIRT_EXIT_CODE 6
#   else
#       define DIRT_EXIT_CODE 4
#   endif
#endif

#ifndef POINTS_EXIT_CODE
#   define POINTS_EXIT_CODE 7
#endif

#ifndef UNEXPECTED_EOF_EXIT_CODE
#   define UNEXPECTED_EOF_EXIT_CODE 8
#endif

#ifndef PC_BASE_EXIT_CODE
#   ifdef TESTSYS
#       define PC_BASE_EXIT_CODE 50
#   else
#       define PC_BASE_EXIT_CODE 0
#   endif
#endif

int resultExitCode(TResult r) {
    if (r == _ok)
        return OK_EXIT_CODE;
    if (r == _wa)
        return WA_EXIT_CODE;
    if (r == _pe)
        return PE_EXIT_CODE;
    if (r == _fail)
        return FAIL_EXIT_CODE;
    if (r == _dirt)
        return DIRT_EXIT_CODE;
    if (r == _points)
        return POINTS_EXIT_CODE;
    if (r == _unexpected_eof)
#ifdef ENABLE_UNEXPECTED_EOF
        return UNEXPECTED_EOF_EXIT_CODE;
#else
        return PE_EXIT_CODE;
#endif
    if (r >= _partially)
        return PC_BASE_EXIT_CODE + (r - _partially);
    return FAIL_EXIT_CODE;
}


switch (result) {
    case _ok:
        errorName = "ok ";
        quitscrS(LightGreen, errorName);
        break;
    case _wa:
        errorName = "wrong answer ";
        quitscrS(LightRed, errorName);
        break;
    case _pe:
        errorName = "wrong output format ";
        quitscrS(LightRed, errorName);
        break;
    case _fail:
        errorName = "FAIL ";
        quitscrS(LightRed, errorName);
        break;
    case _dirt:
        errorName = "wrong output format ";
        quitscrS(LightCyan, errorName);
        result = _pe;
        break;
    case _points:
        errorName = "points ";
        quitscrS(LightYellow, errorName);
        break;
    case _unexpected_eof:
        errorName = "unexpected eof ";
        quitscrS(LightCyan, errorName);
        break;
    default:
        if (result >= _partially) {
            errorName = format("partially correct (%d) ", pctype);
            isPartial = true;
            quitscrS(LightYellow, errorName);
        } else
            quit(_fail, "What is the code ??? ");

其他平台编译参数汇总

luogu:https://www.luogu.com.cn/discuss/86673

codeforces(may be old):https://codeforces.com/blog/entry/79

loj:https://github.com/syzoj/syzoj-ng-judge/tree/master/src/languages

lojv3:https://github.com/syzoj/judge-v3/tree/master/src/languages

uoj:https://github.com/UniversalOJ/UOJ-System/blob/230738b770022cc6b882c42b67b82d7b29b82003/judger/uoj_judger/include/uoj_judger.h#L1137

pta: https://github.com/pintia/ljudge/tree/master/etc/ljudge

todo

  • spj cache LRU