judge: bug fix

master
undefined 11 months ago
parent 1d9369fe55
commit 2d25eaac0f
No known key found for this signature in database

@ -1,7 +1,6 @@
import { LangConfig } from '@hydrooj/utils/lib/lang';
import { STATUS } from '@hydrooj/utils/lib/status';
import { findFileSync } from '@hydrooj/utils/lib/utils';
import checkers from './checkers';
import { CompileError, FormatError } from './error';
import { Execute } from './interface';
import {
@ -51,7 +50,10 @@ const testlibFile = {
src: findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h'),
};
async function _compile(src: string, type: 'checker' | 'validator' | 'interactor', getLang, copyIn, withTestlib = true, next?: any) {
export async function compileWithTestlib(
src: string, type: 'checker' | 'validator' | 'interactor',
getLang, copyIn: CopyIn, withTestlib = true, next?: any,
) {
const s = src.replace('@', '.').split('.');
let lang;
let langId = s.pop();
@ -65,19 +67,3 @@ async function _compile(src: string, type: 'checker' | 'validator' | 'interactor
// TODO cache compiled binary
return await compile(lang, { src }, copyIn, next);
}
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: CopyIn, next?: any) {
if (['default', 'strict'].includes(checkerType)) {
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
}
if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]);
return _compile(checker, 'checker', getLang, copyIn, checkerType === 'testlib', next);
}
export async function compileInteractor(getLang: Function, interactor: string, copyIn: CopyIn) {
return _compile(interactor, 'interactor', getLang, copyIn);
}
export async function compileValidator(getLang: Function, validator: string, copyIn: CopyIn) {
return _compile(validator, 'validator', getLang, copyIn);
}

@ -1,9 +1,6 @@
import { basename } from 'path';
import { STATUS } from '@hydrooj/utils/lib/status';
import checkers from '../checkers';
import compile, { compileChecker } from '../compile';
import { runFlow } from '../flow';
import { Execute } from '../interface';
import { Logger } from '../log';
import { del, runQueued } from '../sandbox';
import signals from '../signals';
@ -101,30 +98,9 @@ function judgeCase(c: NormalizedCase) {
export const judge = async (ctx: Context) => await runFlow(ctx, {
compile: async () => {
const markCleanup = (i: Execute) => {
ctx.clean.push(i.clean);
return i;
};
[ctx.execute, ctx.checker] = await Promise.all([
compile(
ctx.session.getLang(ctx.lang), ctx.code,
Object.fromEntries(
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
),
ctx.next,
).then(markCleanup),
compileChecker(
ctx.session.getLang,
ctx.config.checker_type,
ctx.config.checker,
{
user_code: ctx.code,
...Object.fromEntries(
(ctx.config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
),
},
ctx.next,
).then(markCleanup),
ctx.compile(ctx.lang, ctx.code),
ctx.compileWithTestlib('checker', ctx.config.checker, ctx.config.checker_type),
]);
},
judgeCase,

@ -1,29 +1,18 @@
/* eslint-disable no-sequences */
import { basename } from 'path';
import { fs } from '@hydrooj/utils';
import { STATUS } from '@hydrooj/utils/lib/status';
import checkers from '../checkers';
import compile, { compileChecker, compileValidator } from '../compile';
import { Execute } from '../interface';
import { del, runQueued } from '../sandbox';
import signals from '../signals';
import { parseMemoryMB, parseTimeMS } from '../utils';
import { Context } from './interface';
export async function judge(ctx: Context) {
const { config, session } = ctx;
ctx.next({ status: STATUS.STATUS_COMPILING, progress: 0 });
const userExtraFiles = Object.fromEntries(
(config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
);
const judgeExtraFiles = Object.fromEntries(
(config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
);
const markCleanup = (i: Execute) => (ctx.clean.push(i.clean), i);
const [execute, checker, validator, input] = await Promise.all([
compile(session.getLang(ctx.lang), ctx.code, userExtraFiles, ctx.next).then(markCleanup),
compileChecker(session.getLang, config.checker_type, config.checker, judgeExtraFiles).then(markCleanup),
compileValidator(session.getLang, config.validator, judgeExtraFiles).then(markCleanup),
ctx.compile(ctx.lang, ctx.code),
ctx.compileWithTestlib('checker', ctx.config.checker, ctx.config.checker_type),
ctx.compileWithTestlib('validator', ctx.config.validator),
ctx.session.fetchFile(ctx.files.hack),
]);
ctx.clean.push(() => fs.unlink(input));
@ -32,7 +21,7 @@ export async function judge(ctx: Context) {
validator.execute,
{
stdin: { src: input },
copyIn: { ...validator.copyIn, ...judgeExtraFiles },
copyIn: validator.copyIn,
time: parseTimeMS(ctx.config.time || '1s'),
memory: parseMemoryMB(ctx.config.memory || '256m'),
},

@ -1,8 +1,5 @@
import { basename } from 'path';
import { STATUS } from '@hydrooj/utils/lib/status';
import compile, { compileInteractor } from '../compile';
import { runFlow } from '../flow';
import { Execute } from '../interface';
import { runPiped } from '../sandbox';
import signals from '../signals';
import { parse } from '../testlib';
@ -11,8 +8,6 @@ import { Context, ContextSubTask } from './interface';
function judgeCase(c: NormalizedCase) {
return async (ctx: Context, ctxSubtask: ContextSubTask) => {
ctx.executeInteractor.copyIn.in = c.input ? { src: c.input } : { content: '' };
ctx.executeInteractor.copyIn.out = c.output ? { src: c.output } : { content: '' };
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
const [{
code, signalled, time, memory,
@ -27,7 +22,11 @@ function judgeCase(c: NormalizedCase) {
},
{
execute: `${ctx.executeInteractor.execute} /w/in /w/tout /w/out`,
copyIn: ctx.executeInteractor.copyIn,
copyIn: {
in: c.input ? { src: c.input } : { content: '' },
out: c.output ? { src: c.output } : { content: '' },
...ctx.executeInteractor.copyIn,
},
time: c.time * 2,
memory: c.memory * 2,
copyOut: ['/w/tout?'],
@ -67,20 +66,9 @@ function judgeCase(c: NormalizedCase) {
export const judge = async (ctx: Context) => await runFlow(ctx, {
compile: async () => {
const markCleanup = (i: Execute) => {
ctx.clean.push(i.clean);
return i;
};
const userExtraFiles = Object.fromEntries(
(ctx.config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
);
const interactorFiles = { user_code: ctx.code };
for (const file of ctx.config.judge_extra_files) {
interactorFiles[basename(file)] = { src: file };
}
[ctx.executeUser, ctx.executeInteractor] = await Promise.all([
compile(ctx.session.getLang(ctx.lang), ctx.code, userExtraFiles, ctx.next).then(markCleanup),
compileInteractor(ctx.session.getLang, ctx.config.interactor, interactorFiles).then(markCleanup),
ctx.compile(ctx.lang, ctx.code),
ctx.compileWithTestlib('interactor', ctx.config.interactor),
]);
},
judgeCase,

@ -1,11 +1,11 @@
import { STATUS } from '@hydrooj/utils/lib/status';
import compile from '../compile';
import { CompileError } from '../error';
import { Execute } from '../interface';
import { Logger } from '../log';
import { runQueued } from '../sandbox';
import signals from '../signals';
import { JudgeTask } from '../task';
import { compilerText, parseMemoryMB, parseTimeMS } from '../utils';
import { Context } from './interface';
const failure = (status: number, message?: string) => ({
status,
@ -17,16 +17,16 @@ const failure = (status: number, message?: string) => ({
const logger = new Logger('judge/run');
export const judge = async (ctx: Context) => {
export const judge = async (ctx: JudgeTask) => {
ctx.stat.judge = new Date();
ctx.next({ status: STATUS.STATUS_COMPILING });
let execute: Execute;
try {
ctx.execute = await compile(
ctx.session.getLang(ctx.lang), ctx.code,
execute = await ctx.compile(
ctx.lang, ctx.code,
Object.fromEntries(
(ctx.config.user_extra_files || []).map((i) => [i.split('/').pop(), { src: i }]),
),
ctx.next,
);
} catch (e) {
if (e instanceof CompileError) {
@ -44,14 +44,13 @@ export const judge = async (ctx: Context) => {
}
return;
}
ctx.clean.push(ctx.execute.clean);
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
const { address_space_limit, process_limit } = ctx.session.getLang(ctx.lang);
const res = await runQueued(
ctx.execute.execute,
execute.execute,
{
stdin: { content: ctx.input },
copyIn: ctx.execute.copyIn,
copyIn: execute.copyIn,
// Allow 2x limits for better debugging
time: parseTimeMS(ctx.config.time || '1s') * 2,
memory: parseMemoryMB(ctx.config.memory || '128m'),
@ -90,10 +89,9 @@ export const judge = async (ctx: Context) => {
const langConfig = ctx.session.getLang(ctx.lang);
if (langConfig.analysis) {
try {
ctx.analysis = true;
const r = await runQueued(langConfig.analysis, {
copyIn: {
...ctx.execute.copyIn,
...execute.copyIn,
input: { content: ctx.input },
[langConfig.code_file || 'foo']: ctx.code,
compile: { content: langConfig.compile || '' },

@ -69,17 +69,7 @@ function judgeCase(c: NormalizedCase) {
export const judge = async (ctx: Context) => {
await runFlow(ctx, {
compile: async () => {
const copyIn = { user_code: ctx.code };
for (const file of ctx.config.judge_extra_files) {
copyIn[basename(file)] = { src: file };
}
ctx.checker = await compileChecker(
ctx.session.getLang,
ctx.config.checker_type,
ctx.config.checker,
copyIn,
);
ctx.clean.push(ctx.checker.clean);
ctx.checker = await ctx.compileWithTestlib('checker', ctx.config.checker, ctx.config.checker_type);
},
judgeCase,
});

@ -1,3 +1,4 @@
import { basename } from 'path';
import { fs } from '@hydrooj/utils';
import { LangConfig } from '@hydrooj/utils/lib/lang';
import { STATUS } from '@hydrooj/utils/lib/status';
@ -5,12 +6,14 @@ import type {
FileInfo, JudgeMeta, JudgeRequest, JudgeResultBody, TestCase,
} from 'hydrooj';
import readCases from './cases';
import checkers from './checkers';
import compile, { compileWithTestlib } from './compile';
import { getConfig } from './config';
import { CompileError, FormatError } from './error';
import { NextFunction, ParsedConfig } from './interface';
import judge from './judge';
import { Logger } from './log';
import { CopyInFile } from './sandbox';
import { CopyIn, CopyInFile } from './sandbox';
import { compilerText, md5 } from './utils';
interface Session {
@ -129,4 +132,28 @@ export class JudgeTask {
if (!judge[type]) throw new FormatError('Unrecognized problemType: {0}', [type]);
await judge[type].judge(this);
}
async compile(lang: string, code: CopyInFile) {
const copyIn = Object.fromEntries(
(this.config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
) as CopyIn;
const result = await compile(this.session.getLang(lang), code, copyIn, this.next);
this.clean.push(result.clean);
return result;
}
async compileWithTestlib(type: 'interactor' | 'validator' | 'checker', file: string, checkerType?: string) {
if (type === 'checker' && ['default', 'strict'].includes(checkerType)) return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]);
const withTestlib = type !== 'checker' || checkerType === 'testlib';
const copyIn = {
user_code: this.code,
...Object.fromEntries(
(this.config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
),
} as CopyIn;
const result = await compileWithTestlib(file, type, this.session.getLang, copyIn, withTestlib, this.next);
this.clean.push(result.clean);
return result;
}
}

Loading…
Cancel
Save