tioj-judge
is the new judge client of TIOJ. It is based on cjail code execution engine, and implements submission scheduling, testdata management and interaction logic with the web server.
- Limit memory by VSS and/or RSS
- Detailed execution stats compared to miku, including verdicts such as SIG, OLE, MLE and VSS reporting
- Judge multiple submissions in parallel
- Isolated and size-limited tmpfs to prevent DoS attacks
- New powerful special judge mode that can access lots of information, set arbitrary score and override results
- Multistage problems (similar to CMS's TwoStep mode)
- Optional strict mode for more isolation:
- Compiles static executables and run them without any shared libraries present (only for C/C++/Haskell)
- Use pipes for standard input / output to avoid seeking or re-opening
- A library interface
libtioj
that can be used independently - A time multiplier to compensate speed difference of multiple judge clients
- Support some basic judging modes (such as floating-point compare, strict compare and white-diff compare) without the need of user-provided special judge program
- An option to skip testdata once any of the testdata in the group got non-AC
On Ubuntu 22.04, you can use the following command to install the dependencies.
apt update
apt install -y git g++ cmake ninja-build \
libseccomp-dev libnl-genl-3-dev libsqlite3-dev libz-dev libssl-dev \
libboost-all-dev libzstd-dev \
ghc python2 python3 python3-numpy python3-pil
mkdir build
cd build
cmake -G Ninja ..
ninja
sudo ninja install
This will also install libtioj
and its dependencies (namely nlohmann_json
and cjail
). Specify -DTIOJ_INSTALL_LIBTIOJ=0
if only the judge client is needed.
Set up the /etc/tioj-judge.conf
configuration file, and then run sudo tioj-judge
to start the judge. The configuration file format is as follows:
tioj_url = https://url.to.tioj.web.server
tioj_key = some_random_key
parallel = 1
max_rss_per_task_mb = 2048
max_output_per_task_mb = 1024
max_submission_queue_size = 20
time_multiplier = 1.0
pinned_cpus = none
box_root = /tmp/tioj_box
submission_root = /tmp/tioj_submissions
testdata_root = /var/lib/
- The indicated values except
tioj_url
,tioj_key
are the default values. time_multiplier
is the ratio of the indicated time to the real time. Thus, the multiplier should be larger if the computer is faster, and smaller if the computer is slower.pinned_cpus
can be a list of CPUs using the same format used in thecpuset
's-c
option (e.g.0,2-3,6-9:2
), or simplyall
ornone
. If this option is specified, each task (including compiling, execution, etc.) will be pinned to one of the provided CPUs.box_root
,submission_root
andtestdata_root
represent the paths for the execution sandbox, submission files, and the storage of downloaded testdata and other persistent information, respectively.- Multiple judge clients can be run at the same time by using the
-c
command-line option to specify different paths for each client. It's important to note that unexpected errors could arise if any of these three paths are shared among multiple judge clients.
- Multiple judge clients can be run at the same time by using the
A Dockerfile is provided to run the judge easily.
When running the docker, --privileged --net=host --pid=host
must be given, and the fetch key should be given via environment variable TIOJ_KEY
.
A minimal working C++ example:
#include <cstdio>
#include <tioj/paths.h>
#include <tioj/utils.h>
#include <tioj/submission.h>
// Callbacks for judge results
class MyReporter : public Reporter {
void ReportOverallResult(const Submission& sub) override {
printf("Result: %s\n", VerdictToAbr(sub.verdict));
}
void ReportCEMessage(const Submission& sub) override {
puts("CE message:");
printf("%s\n", sub.ce_message.c_str());
}
} reporter;
int main() {
// run as root
constexpr int submission_id = 1, problem_id = 1, num_td = 3;
Submission sub;
sub.submission_internal_id = GetUniqueSubmissionInternalId();
sub.submission_id = submission_id;
sub.lang = Compiler::GCC_CPP_17;
sub.problem_id = problem_id;
sub.specjudge_type = SpecjudgeType::NORMAL;
sub.interlib_type = InterlibType::NONE;
for (int i = 0; i < num_td; i++) {
// submission files & limits
Submission::TestdataItem td;
td.input_file = "/path/to/input";
td.answer_file = "/path/to/output";
td.vss = 65536; // 64 KiB
td.rss = 0;
td.output = 65536;
td.time = 1000000; // 1 sec
sub.testdata.push_back(td);
}
sub.remove_submission = true;
sub.reporter = &reporter;
{ // submission files
// mkdir SubmissionCodePath(sub.submission_internal_id)
// write submission code into SubmissionUserCode(sub.submission_internal_id)
}
// note that all paths can be symbolic links
PushSubmission(std::move(sub));
WorkLoop(false); // keep judging until all submissions in the queue are finished
}
Remember to link -ltioj -lpthread
when compiling.