mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
judge: set higher priority for compile task
This commit is contained in:
9 files changed
+31
-29
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable no-template-curly-in-string */
|
/* eslint-disable no-template-curly-in-string */
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import { FormatError, SystemError } from './error';
|
import { FormatError, SystemError } from './error';
|
||||||
import { CopyInFile, run } from './sandbox';
|
import { CopyInFile, runQueued } from './sandbox';
|
||||||
import { parse } from './testlib';
|
import { parse } from './testlib';
|
||||||
|
|
||||||
export interface CheckConfig {
|
export interface CheckConfig {
|
||||||
@@ -54,7 +54,7 @@ function parseDiffMsg(msg: string) {
|
|||||||
|
|
||||||
const checkers: Record<string, Checker> = new Proxy({
|
const checkers: Record<string, Checker> = new Proxy({
|
||||||
async default(config) {
|
async default(config) {
|
||||||
const { stdout } = await run('/usr/bin/diff -BZ usrout answer', {
|
const { stdout } = await runQueued('/usr/bin/diff -BZ usrout answer', {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
answer: config.output,
|
answer: config.output,
|
||||||
@@ -76,7 +76,7 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async strict(config) {
|
async strict(config) {
|
||||||
const { stdout } = await run('/usr/bin/diff usrout answer', {
|
const { stdout } = await runQueued('/usr/bin/diff usrout answer', {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
answer: config.output,
|
answer: config.output,
|
||||||
@@ -98,7 +98,7 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
* exit code:返回判断结果
|
* exit code:返回判断结果
|
||||||
*/
|
*/
|
||||||
async hustoj(config) {
|
async hustoj(config) {
|
||||||
const { code, stdout } = await run(`${config.execute} input answer usrout`, {
|
const { code, stdout } = await runQueued(`${config.execute} input answer usrout`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
answer: config.output,
|
answer: config.output,
|
||||||
@@ -123,7 +123,7 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
* argv[6]:输出错误报告的文件
|
* argv[6]:输出错误报告的文件
|
||||||
*/
|
*/
|
||||||
async lemon(config) {
|
async lemon(config) {
|
||||||
const { files, code } = await run(`${config.execute} input usrout answer ${config.score} score message`, {
|
const { files, code } = await runQueued(`${config.execute} input usrout answer ${config.score} score message`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
answer: config.output,
|
answer: config.output,
|
||||||
@@ -156,7 +156,7 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
* exit code:返回判断结果
|
* exit code:返回判断结果
|
||||||
*/
|
*/
|
||||||
async qduoj(config) {
|
async qduoj(config) {
|
||||||
const { status, stdout } = await run(`${config.execute} input usrout`, {
|
const { status, stdout } = await runQueued(`${config.execute} input usrout`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
usrout: config.user_stdout,
|
usrout: config.user_stdout,
|
||||||
input: config.input,
|
input: config.input,
|
||||||
@@ -182,7 +182,7 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
*/
|
*/
|
||||||
async syzoj(config) {
|
async syzoj(config) {
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let { status, stdout, stderr } = await run(config.execute, {
|
let { status, stdout, stderr } = await runQueued(config.execute, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
input: config.input,
|
input: config.input,
|
||||||
user_out: config.user_stdout,
|
user_out: config.user_stdout,
|
||||||
@@ -198,7 +198,7 @@ const checkers: Record<string, Checker> = new Proxy({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async testlib(config) {
|
async testlib(config) {
|
||||||
const { stderr, status, code } = await run(`${config.execute} /w/in /w/user_out /w/answer`, {
|
const { stderr, status, code } = await runQueued(`${config.execute} /w/in /w/user_out /w/answer`, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
in: config.input,
|
in: config.input,
|
||||||
user_out: config.user_stdout,
|
user_out: config.user_stdout,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import checkers from './checkers';
|
|||||||
import { CompileError, FormatError } from './error';
|
import { CompileError, FormatError } from './error';
|
||||||
import { Execute } from './interface';
|
import { Execute } from './interface';
|
||||||
import {
|
import {
|
||||||
CopyIn, CopyInFile, del, run,
|
CopyIn, CopyInFile, del, runQueued,
|
||||||
} from './sandbox';
|
} from './sandbox';
|
||||||
import { compilerText } from './utils';
|
import { compilerText } from './utils';
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export default async function compile(
|
|||||||
if (lang.compile) {
|
if (lang.compile) {
|
||||||
const {
|
const {
|
||||||
status, stdout, stderr, fileIds,
|
status, stdout, stderr, fileIds,
|
||||||
} = await run(
|
} = await runQueued(
|
||||||
copyIn['compile.sh'] ? '/bin/bash compile.sh' : lang.compile,
|
copyIn['compile.sh'] ? '/bin/bash compile.sh' : lang.compile,
|
||||||
{
|
{
|
||||||
copyIn: { ...copyIn, [lang.code_file]: code },
|
copyIn: { ...copyIn, [lang.code_file]: code },
|
||||||
@@ -26,6 +26,7 @@ export default async function compile(
|
|||||||
time: lang.compile_time_limit || 10000,
|
time: lang.compile_time_limit || 10000,
|
||||||
memory: lang.compile_memory_limit || 256 * 1024 * 1024,
|
memory: lang.compile_memory_limit || 256 * 1024 * 1024,
|
||||||
},
|
},
|
||||||
|
3,
|
||||||
);
|
);
|
||||||
if (status === STATUS.STATUS_TIME_LIMIT_EXCEEDED) next?.({ message: 'Compile timeout.' });
|
if (status === STATUS.STATUS_TIME_LIMIT_EXCEEDED) next?.({ message: 'Compile timeout.' });
|
||||||
if (status === STATUS.STATUS_MEMORY_LIMIT_EXCEEDED) next?.({ message: 'Compile memory limit exceeded.' });
|
if (status === STATUS.STATUS_MEMORY_LIMIT_EXCEEDED) next?.({ message: 'Compile memory limit exceeded.' });
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const JudgeSettings = Schema.object({
|
|||||||
total_time_limit: Schema.number().default(60),
|
total_time_limit: Schema.number().default(60),
|
||||||
processLimit: Schema.number().default(32),
|
processLimit: Schema.number().default(32),
|
||||||
parallelism: Schema.number().default(2),
|
parallelism: Schema.number().default(2),
|
||||||
|
concurrency: Schema.number(),
|
||||||
singleTaskParallelism: Schema.number().default(2),
|
singleTaskParallelism: Schema.number().default(2),
|
||||||
rerun: Schema.number().description('Re-Run testcase if time-limit-exceeded (max per submission)').default(0),
|
rerun: Schema.number().description('Re-Run testcase if time-limit-exceeded (max per submission)').default(0),
|
||||||
rate: Schema.number().default(1),
|
rate: Schema.number().default(1),
|
||||||
|
|||||||
@@ -80,8 +80,9 @@ export async function postInit() {
|
|||||||
logger.debug('Record not found: %o', t);
|
logger.debug('Record not found: %o', t);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(new JudgeTask(session, Object.assign(rdoc, t))).handle().catch(logger.error);
|
await (new JudgeTask(session, Object.assign(rdoc, t))).handle().catch(logger.error);
|
||||||
};
|
};
|
||||||
TaskModel.consume({ type: 'judge' }, handle);
|
const parallelism = Math.max(getConfig('parallelism'), 2);
|
||||||
|
for (let i = 1; i < parallelism; i++) TaskModel.consume({ type: 'judge' }, handle);
|
||||||
TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle);
|
TaskModel.consume({ type: 'judge', priority: { $gt: -50 } }, handle);
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import compile, { compileChecker } from '../compile';
|
|||||||
import { runFlow } from '../flow';
|
import { runFlow } from '../flow';
|
||||||
import { Execute } from '../interface';
|
import { Execute } from '../interface';
|
||||||
import { Logger } from '../log';
|
import { Logger } from '../log';
|
||||||
import { del, run } from '../sandbox';
|
import { del, run, runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { NormalizedCase } from '../utils';
|
import { NormalizedCase } from '../utils';
|
||||||
import { Context, ContextSubTask } from './interface';
|
import { Context, ContextSubTask } from './interface';
|
||||||
@@ -14,7 +14,7 @@ const logger = new Logger('judge/default');
|
|||||||
|
|
||||||
function judgeCase(c: NormalizedCase) {
|
function judgeCase(c: NormalizedCase) {
|
||||||
return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => {
|
return async (ctx: Context, ctxSubtask: ContextSubTask, runner?: Function) => {
|
||||||
const res = await run(
|
const res = await runQueued(
|
||||||
ctx.execute.execute,
|
ctx.execute.execute,
|
||||||
{
|
{
|
||||||
stdin: { src: c.input },
|
stdin: { src: c.input },
|
||||||
@@ -63,7 +63,7 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
if (langConfig.analysis && !ctx.analysis) {
|
if (langConfig.analysis && !ctx.analysis) {
|
||||||
ctx.analysis = true;
|
ctx.analysis = true;
|
||||||
try {
|
try {
|
||||||
const r = await run(langConfig.analysis, {
|
const r = await runQueued(langConfig.analysis, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
...ctx.execute.copyIn,
|
...ctx.execute.copyIn,
|
||||||
input: { src: c.input },
|
input: { src: c.input },
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
|
|||||||
import checkers from '../checkers';
|
import checkers from '../checkers';
|
||||||
import compile, { compileChecker, compileValidator } from '../compile';
|
import compile, { compileChecker, compileValidator } from '../compile';
|
||||||
import { Execute } from '../interface';
|
import { Execute } from '../interface';
|
||||||
import { del, run } from '../sandbox';
|
import { del, run, runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { parseMemoryMB, parseTimeMS } from '../utils';
|
import { parseMemoryMB, parseTimeMS } from '../utils';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
@@ -28,7 +28,7 @@ export async function judge(ctx: Context) {
|
|||||||
]);
|
]);
|
||||||
ctx.clean.push(() => fs.unlink(input));
|
ctx.clean.push(() => fs.unlink(input));
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
||||||
const validateResult = await run(
|
const validateResult = await runQueued(
|
||||||
validator.execute,
|
validator.execute,
|
||||||
{
|
{
|
||||||
stdin: { src: input },
|
stdin: { src: input },
|
||||||
@@ -41,7 +41,7 @@ export async function judge(ctx: Context) {
|
|||||||
const message = `${validateResult.stdout || ''}\n${validateResult.stderr || ''}`.trim();
|
const message = `${validateResult.stdout || ''}\n${validateResult.stderr || ''}`.trim();
|
||||||
return ctx.end({ status: STATUS.STATUS_FORMAT_ERROR, message });
|
return ctx.end({ status: STATUS.STATUS_FORMAT_ERROR, message });
|
||||||
}
|
}
|
||||||
const res = await run(
|
const res = await runQueued(
|
||||||
execute.execute,
|
execute.execute,
|
||||||
{
|
{
|
||||||
stdin: { src: input },
|
stdin: { src: input },
|
||||||
@@ -76,9 +76,7 @@ export async function judge(ctx: Context) {
|
|||||||
if (code < 32) message = signals[code];
|
if (code < 32) message = signals[code];
|
||||||
else message = { message: 'Your program returned {0}.', params: [code] };
|
else message = { message: 'Your program returned {0}.', params: [code] };
|
||||||
}
|
}
|
||||||
await Promise.all(
|
await Promise.allSettled(Object.values(res.fileIds).map((id) => del(id)));
|
||||||
Object.values(res.fileIds).map((id) => del(id)),
|
|
||||||
).catch(() => { /* Ignore file doesn't exist */ });
|
|
||||||
|
|
||||||
if (message) ctx.next({ message });
|
if (message) ctx.next({ message });
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
|
|||||||
import compile from '../compile';
|
import compile from '../compile';
|
||||||
import { CompileError } from '../error';
|
import { CompileError } from '../error';
|
||||||
import { Logger } from '../log';
|
import { Logger } from '../log';
|
||||||
import { run } from '../sandbox';
|
import { runQueued } from '../sandbox';
|
||||||
import signals from '../signals';
|
import signals from '../signals';
|
||||||
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
|
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
@@ -46,7 +46,7 @@ export const judge = async (ctx: Context) => {
|
|||||||
}
|
}
|
||||||
ctx.clean.push(ctx.execute.clean);
|
ctx.clean.push(ctx.execute.clean);
|
||||||
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
|
||||||
const res = await run(
|
const res = await runQueued(
|
||||||
ctx.execute.execute,
|
ctx.execute.execute,
|
||||||
{
|
{
|
||||||
stdin: { content: ctx.input },
|
stdin: { content: ctx.input },
|
||||||
@@ -55,6 +55,7 @@ export const judge = async (ctx: Context) => {
|
|||||||
time: parseTimeMS(ctx.config.time || '1s') * 2,
|
time: parseTimeMS(ctx.config.time || '1s') * 2,
|
||||||
memory: parseMemoryMB(ctx.config.memory || '128m'),
|
memory: parseMemoryMB(ctx.config.memory || '128m'),
|
||||||
},
|
},
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
const { code, time, memory } = res;
|
const { code, time, memory } = res;
|
||||||
let { status } = res;
|
let { status } = res;
|
||||||
@@ -85,7 +86,7 @@ export const judge = async (ctx: Context) => {
|
|||||||
if (langConfig.analysis) {
|
if (langConfig.analysis) {
|
||||||
try {
|
try {
|
||||||
ctx.analysis = true;
|
ctx.analysis = true;
|
||||||
const r = await run(langConfig.analysis, {
|
const r = await runQueued(langConfig.analysis, {
|
||||||
copyIn: {
|
copyIn: {
|
||||||
...ctx.execute.copyIn,
|
...ctx.execute.copyIn,
|
||||||
input: { content: ctx.input },
|
input: { content: ctx.input },
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
|
|||||||
import checkers from '../checkers';
|
import checkers from '../checkers';
|
||||||
import { compileChecker } from '../compile';
|
import { compileChecker } from '../compile';
|
||||||
import { runFlow } from '../flow';
|
import { runFlow } from '../flow';
|
||||||
import { del, run } from '../sandbox';
|
import { del, runQueued } from '../sandbox';
|
||||||
import { NormalizedCase } from '../utils';
|
import { NormalizedCase } from '../utils';
|
||||||
import { Context } from './interface';
|
import { Context } from './interface';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
let score = 0;
|
let score = 0;
|
||||||
const fileIds = [];
|
const fileIds = [];
|
||||||
if (ctx.config.subType === 'multi') {
|
if (ctx.config.subType === 'multi') {
|
||||||
const res = await run(
|
const res = await runQueued(
|
||||||
'/usr/bin/unzip foo.zip',
|
'/usr/bin/unzip foo.zip',
|
||||||
{
|
{
|
||||||
stdin: null,
|
stdin: null,
|
||||||
@@ -54,7 +54,7 @@ function judgeCase(c: NormalizedCase) {
|
|||||||
env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() },
|
env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() },
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
await Promise.all(fileIds.map(del)).catch(() => { /* Ignore file doesn't exist */ });
|
await Promise.allSettled(fileIds.map(del));
|
||||||
return {
|
return {
|
||||||
id: c.id,
|
id: c.id,
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -206,10 +206,10 @@ export async function run(execute: string, params?: Parameter): Promise<SandboxA
|
|||||||
return await adaptResult(result, params);
|
return await adaptResult(result, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
const queue = new PQueue({ concurrency: getConfig('parallelism') });
|
const queue = new PQueue({ concurrency: getConfig('concurrency') || getConfig('parallelism') });
|
||||||
|
|
||||||
export function runQueued(execute: string, params?: Parameter, priority = 0) {
|
export function runQueued(execute: string, params?: Parameter, priority = 0) {
|
||||||
return queue.add(() => run(execute, params), { priority });
|
return queue.add(() => run(execute, params), { priority }) as Promise<SandboxAdaptedResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from './sandbox/interface';
|
export * from './sandbox/interface';
|
||||||
Reference in new issue
Block a user