mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
judge: merge processTestdata
This commit is contained in:
1 parent
e4b916037c
commit
b74f6b64f8
15 files changed
+75
-149
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/hydrojudge",
|
"name": "@hydrooj/hydrojudge",
|
||||||
"bin": "bin/hydrojudge.js",
|
"bin": "bin/hydrojudge.js",
|
||||||
"version": "2.20.1",
|
"version": "2.20.2",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
|
|||||||
@@ -37,11 +37,6 @@ memoryMax:
|
|||||||
type: text
|
type: text
|
||||||
name: memoryMax
|
name: memoryMax
|
||||||
default: 512m
|
default: 512m
|
||||||
retry_delay_sec:
|
|
||||||
type: number
|
|
||||||
name: retry_delay_sec
|
|
||||||
desc: Retry delay sec
|
|
||||||
default: 15
|
|
||||||
cache_dir:
|
cache_dir:
|
||||||
type: text
|
type: text
|
||||||
name: cache_dir
|
name: cache_dir
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
@@ -35,6 +36,32 @@ async function collectFiles(folder: string) {
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function processTestdata(folder: string) {
|
||||||
|
let files = await fs.readdir(folder);
|
||||||
|
if (files.length <= 2) {
|
||||||
|
if (files.length === 2) files.splice(files.indexOf('version'), 1);
|
||||||
|
if (fs.statSync(path.resolve(folder, files[0])).isDirectory()) {
|
||||||
|
folder = path.resolve(folder, files[0]);
|
||||||
|
files = await fs.readdir(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ini = files.filter((i) => i.toLowerCase() === 'config.ini')[0];
|
||||||
|
if (!ini) return;
|
||||||
|
const t = fs.readFileSync(path.resolve(folder, ini), 'utf8');
|
||||||
|
await fs.writeFile(path.resolve(folder, 'config.ini'), t.toLowerCase());
|
||||||
|
for (const i of files) {
|
||||||
|
if (i.toLowerCase() === 'input') await fs.rename(`${folder}/${i}`, `${folder}/input`);
|
||||||
|
if (i.toLowerCase() === 'output') await fs.rename(`${folder}/${i}`, `${folder}/output`);
|
||||||
|
}
|
||||||
|
await Promise.all(['input', 'output'].flatMap(async (f) => {
|
||||||
|
const dir = path.resolve(folder, f);
|
||||||
|
const sf = await fs.readdir(dir);
|
||||||
|
return sf
|
||||||
|
.filter((i) => i !== i.toLowerCase())
|
||||||
|
.map((i) => fs.rename(`${dir}/${i}`, `${dir}/${i.toLowerCase()}`));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
export default async function readCases(folder: string, cfg: Record<string, any> = {}, args) {
|
export default async function readCases(folder: string, cfg: Record<string, any> = {}, args) {
|
||||||
const iniConfig = path.resolve(folder, 'config.ini');
|
const iniConfig = path.resolve(folder, 'config.ini');
|
||||||
const yamlConfig = path.resolve(folder, 'config.yaml');
|
const yamlConfig = path.resolve(folder, 'config.yaml');
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ export async function check(config: CheckConfig): Promise<[number, number, strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise<Execute> {
|
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise<Execute> {
|
||||||
|
if (['default', 'strict'].includes(checkerType)) {
|
||||||
|
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
||||||
|
}
|
||||||
if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]);
|
if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]);
|
||||||
if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc };
|
if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc };
|
||||||
const s = checker.replace('@', '.').split('.');
|
const s = checker.replace('@', '.').split('.');
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable prefer-const */
|
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import cac from 'cac';
|
import cac from 'cac';
|
||||||
@@ -9,19 +8,19 @@ const argv = cac().parse();
|
|||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
let CONFIG_FILE = path.resolve(os.homedir(), '.config', 'hydro', 'judge.yaml');
|
let CONFIG_FILE = path.resolve(os.homedir(), '.config', 'hydro', 'judge.yaml');
|
||||||
|
|
||||||
let config = {
|
const config = {
|
||||||
cache_dir: path.resolve(os.homedir(), '.cache', 'hydro', 'judge'),
|
cache_dir: path.resolve(os.homedir(), '.cache', 'hydro', 'judge'),
|
||||||
tmp_dir: path.resolve(os.tmpdir(), 'hydro', 'judge'),
|
tmp_dir: path.resolve(os.tmpdir(), 'hydro', 'judge'),
|
||||||
tmpfs_size: '256m',
|
tmpfs_size: '256m',
|
||||||
stdio_size: '32m',
|
stdio_size: '32m',
|
||||||
memoryMax: '512m',
|
memoryMax: '512m',
|
||||||
strict_memory: false,
|
strict_memory: false,
|
||||||
retry_delay_sec: 15,
|
|
||||||
sandbox_host: 'http://localhost:5050',
|
sandbox_host: 'http://localhost:5050',
|
||||||
testcases_max: 100,
|
testcases_max: 100,
|
||||||
total_time_limit: 60,
|
total_time_limit: 60,
|
||||||
processLimit: 32,
|
processLimit: 32,
|
||||||
parallelism: 2,
|
parallelism: 2,
|
||||||
|
singleTaskParallelism: 2,
|
||||||
rerun: 0,
|
rerun: 0,
|
||||||
rate: 1,
|
rate: 1,
|
||||||
config: null,
|
config: null,
|
||||||
@@ -29,10 +28,11 @@ let config = {
|
|||||||
env: `\
|
env: `\
|
||||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
HOME=/w`,
|
HOME=/w`,
|
||||||
|
hosts: {},
|
||||||
secret: String.random(32),
|
secret: String.random(32),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getConfig(key: string) {
|
export function getConfig(key: keyof typeof config) {
|
||||||
return global.Hydro ? global.Hydro.model.system.get(`hydrojudge.${key}`) : config[key];
|
return global.Hydro ? global.Hydro.model.system.get(`hydrojudge.${key}`) : config[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,5 +61,5 @@ if (!global.Hydro) {
|
|||||||
config.sandbox_host = path.resolve(process.env.EXECUTION_HOST || argv.options.sandbox);
|
config.sandbox_host = path.resolve(process.env.EXECUTION_HOST || argv.options.sandbox);
|
||||||
}
|
}
|
||||||
const configFile = fs.readFileSync(CONFIG_FILE).toString();
|
const configFile = fs.readFileSync(CONFIG_FILE).toString();
|
||||||
config = { ...config, ...yaml.load(configFile) as any };
|
Object.assign(config, yaml.load(configFile) as any);
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ import './utils';
|
|||||||
import { getConfig } from './config';
|
import { getConfig } from './config';
|
||||||
import * as Session from './hosts/index';
|
import * as Session from './hosts/index';
|
||||||
import log from './log';
|
import log from './log';
|
||||||
import { Queue, sleep } from './utils';
|
import { Queue } from './utils';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
@@ -28,8 +28,8 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!global.onDestroy) global.onDestroy = [];
|
global.onDestroy ||= [];
|
||||||
if (!global.hosts) global.hosts = [];
|
global.hosts ||= [];
|
||||||
let exit = false;
|
let exit = false;
|
||||||
|
|
||||||
const terminate = async () => {
|
const terminate = async () => {
|
||||||
@@ -60,28 +60,16 @@ async function worker(queue: Queue<any>) {
|
|||||||
|
|
||||||
async function daemon() {
|
async function daemon() {
|
||||||
const _hosts = getConfig('hosts');
|
const _hosts = getConfig('hosts');
|
||||||
const delay = getConfig('retry_delay_sec');
|
|
||||||
const hosts = {};
|
const hosts = {};
|
||||||
const queue = new Queue<any>();
|
const queue = new Queue<any>();
|
||||||
|
worker(queue).catch((e) => log.error(e));
|
||||||
for (const i in _hosts) {
|
for (const i in _hosts) {
|
||||||
_hosts[i].host = _hosts[i].host || i;
|
_hosts[i].host = _hosts[i].host || i;
|
||||||
hosts[i] = new Session[_hosts[i].type || 'vj4'](_hosts[i]);
|
hosts[i] = new Session[_hosts[i].type || 'hydro'](_hosts[i]);
|
||||||
await hosts[i].init();
|
await hosts[i].init();
|
||||||
}
|
}
|
||||||
|
for (const i in hosts) hosts[i].consume(queue);
|
||||||
global.hosts = hosts;
|
global.hosts = hosts;
|
||||||
worker(queue);
|
|
||||||
for (const i in hosts) {
|
|
||||||
while ('Orz twd2') {
|
|
||||||
try {
|
|
||||||
await hosts[i].consume(queue);
|
|
||||||
break;
|
|
||||||
} catch (e) {
|
|
||||||
log.error(e, e.stack);
|
|
||||||
log.info(`在 ${delay} 秒后重试`);
|
|
||||||
await sleep(delay * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) daemon();
|
if (require.main === module) daemon();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export class CompileError extends Error {
|
|||||||
stdout: string;
|
stdout: string;
|
||||||
stderr: string;
|
stderr: string;
|
||||||
status: string;
|
status: string;
|
||||||
type: string;
|
type = 'CompileError';
|
||||||
|
|
||||||
constructor(obj: string | CompileErrorInfo) {
|
constructor(obj: string | CompileErrorInfo) {
|
||||||
super('Compile Error');
|
super('Compile Error');
|
||||||
@@ -17,48 +17,29 @@ export class CompileError extends Error {
|
|||||||
this.stderr = obj.stderr || '';
|
this.stderr = obj.stderr || '';
|
||||||
this.status = obj.status ? STATUS_TEXTS[obj.status] || '' : '';
|
this.status = obj.status ? STATUS_TEXTS[obj.status] || '' : '';
|
||||||
}
|
}
|
||||||
this.type = 'CompileError';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FormatError extends Error {
|
export class FormatError extends Error {
|
||||||
type: string;
|
type = 'FormatError';
|
||||||
params: any[];
|
|
||||||
|
|
||||||
constructor(message: string, params = []) {
|
constructor(message: string, public params = []) {
|
||||||
super(message);
|
super(message);
|
||||||
this.type = 'FormatError';
|
|
||||||
this.params = params;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RuntimeError extends Error {
|
export class RuntimeError extends Error {
|
||||||
type: string;
|
type = 'RuntimeError';
|
||||||
detail: any;
|
|
||||||
|
|
||||||
constructor(detail: string, message: string) {
|
constructor(public detail: string, message: string) {
|
||||||
super(message);
|
super(message);
|
||||||
this.type = 'RuntimeError';
|
|
||||||
this.detail = detail;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SystemError extends Error {
|
export class SystemError extends Error {
|
||||||
type: string;
|
type = 'SystemError';
|
||||||
params: any[];
|
|
||||||
|
|
||||||
constructor(message: string, params = []) {
|
constructor(message: string, public params = []) {
|
||||||
super(message);
|
super(message);
|
||||||
this.type = 'SystemError';
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TooFrequentError extends Error {
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
this.type = 'TooFrequentError';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ import WebSocket from 'ws';
|
|||||||
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import type { JudgeResultBody } from 'hydrooj';
|
import type { JudgeResultBody } from 'hydrooj';
|
||||||
import readCases from '../cases';
|
import readCases, { processTestdata } from '../cases';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
import { CompileError, FormatError, SystemError } from '../error';
|
import { CompileError, FormatError, SystemError } from '../error';
|
||||||
import judge from '../judge';
|
import judge from '../judge';
|
||||||
@@ -232,7 +232,7 @@ export default class Hydro {
|
|||||||
queue.start();
|
queue.start();
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
fs.writeFileSync(path.join(filePath, 'etags'), JSON.stringify(version));
|
fs.writeFileSync(path.join(filePath, 'etags'), JSON.stringify(version));
|
||||||
await this.processData(filePath).catch(noop);
|
await processTestdata(filePath);
|
||||||
}
|
}
|
||||||
fs.writeFileSync(path.join(filePath, 'lastUsage'), new Date().getTime().toString());
|
fs.writeFileSync(path.join(filePath, 'lastUsage'), new Date().getTime().toString());
|
||||||
return filePath;
|
return filePath;
|
||||||
@@ -332,34 +332,6 @@ export default class Hydro {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async processData(folder: string) { // eslint-disable-line class-methods-use-this
|
|
||||||
let files = await fs.readdir(folder);
|
|
||||||
let ini = false;
|
|
||||||
if (files.length <= 2) {
|
|
||||||
if (files.length === 2) files.splice(files.indexOf('version'), 1);
|
|
||||||
const s = fs.statSync(path.resolve(folder, files[0]));
|
|
||||||
if (s.isDirectory()) folder = path.resolve(folder, files[0]);
|
|
||||||
}
|
|
||||||
files = await fs.readdir(folder);
|
|
||||||
for (const i of files) {
|
|
||||||
if (i.toLowerCase() === 'config.ini') {
|
|
||||||
ini = true;
|
|
||||||
await fs.rename(`${folder}/${i}`, `${folder}/config.ini`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ini) {
|
|
||||||
for (const i of files) {
|
|
||||||
if (i.toLowerCase() === 'input') await fs.rename(`${folder}/${i}`, `${folder}/input`);
|
|
||||||
else if (i.toLowerCase() === 'output') await fs.rename(`${folder}/${i}`, `${folder}/output`);
|
|
||||||
}
|
|
||||||
files = await fs.readdir(`${folder}/input`);
|
|
||||||
for (const i of files) await fs.rename(`${folder}/input/${i}`, `${folder}/input/${i.toLowerCase()}`);
|
|
||||||
files = await fs.readdir(`${folder}/output`);
|
|
||||||
for (const i of files) await fs.rename(`${folder}/output/${i}`, `${folder}/output/${i.toLowerCase()}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async retry(queue: Queue<any>) {
|
async retry(queue: Queue<any>) {
|
||||||
this.consume(queue).catch(() => {
|
this.consume(queue).catch(() => {
|
||||||
setTimeout(() => this.retry(queue), 30000);
|
setTimeout(() => this.retry(queue), 30000);
|
||||||
|
|||||||
@@ -177,21 +177,17 @@ export const judge = async (ctx: Context, startPromise = Promise.resolve()) => {
|
|||||||
),
|
),
|
||||||
ctx.next,
|
ctx.next,
|
||||||
),
|
),
|
||||||
(() => {
|
compileChecker(
|
||||||
if (['default', 'strict'].includes(ctx.config.checker_type || 'default')) {
|
|
||||||
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
|
||||||
}
|
|
||||||
const copyIn = { user_code: ctx.code };
|
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
|
||||||
}
|
|
||||||
return compileChecker(
|
|
||||||
ctx.getLang,
|
ctx.getLang,
|
||||||
ctx.config.checker_type,
|
ctx.config.checker_type,
|
||||||
ctx.config.checker,
|
ctx.config.checker,
|
||||||
copyIn,
|
{
|
||||||
);
|
user_code: ctx.code,
|
||||||
})(),
|
...Object.fromEntries(
|
||||||
|
(ctx.config.judge_extra_files || []).map((i) => [parseFilename(i), { src: i }]),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
ctx.clean.push(ctx.execute.clean, ctx.checker.clean);
|
ctx.clean.push(ctx.execute.clean, ctx.checker.clean);
|
||||||
await startPromise;
|
await startPromise;
|
||||||
@@ -202,7 +198,7 @@ export const judge = async (ctx: Context, startPromise = Promise.resolve()) => {
|
|||||||
ctx.total_memory_usage_kb = 0;
|
ctx.total_memory_usage_kb = 0;
|
||||||
ctx.total_time_usage_ms = 0;
|
ctx.total_time_usage_ms = 0;
|
||||||
ctx.rerun = getConfig('rerun') || 0;
|
ctx.rerun = getConfig('rerun') || 0;
|
||||||
ctx.queue = new Queue({ concurrency: getConfig('parallelism') });
|
ctx.queue = new Queue({ concurrency: getConfig('singleTaskParallelism') });
|
||||||
ctx.failed = {};
|
ctx.failed = {};
|
||||||
for (const sid in ctx.config.subtasks) tasks.push(judgeSubtask(ctx.config.subtasks[sid], sid)(ctx));
|
for (const sid in ctx.config.subtasks) tasks.push(judgeSubtask(ctx.config.subtasks[sid], sid)(ctx));
|
||||||
const scores = await Promise.all(tasks);
|
const scores = await Promise.all(tasks);
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ export interface Config {
|
|||||||
detail?: boolean;
|
detail?: boolean;
|
||||||
filename?: string;
|
filename?: string;
|
||||||
|
|
||||||
judge_extra_files?: string[];
|
judge_extra_files: string[];
|
||||||
user_extra_files?: string[];
|
user_extra_files: string[];
|
||||||
template?: Record<string, [string, string]>;
|
template?: Record<string, [string, string]>;
|
||||||
|
|
||||||
checker?: string;
|
checker?: string;
|
||||||
|
|||||||
@@ -122,9 +122,6 @@ export const judge = async (ctx: Context, startPromise = Promise.resolve()) => {
|
|||||||
if (!ctx.config.subtasks.length) throw new FormatError('Problem data not found.');
|
if (!ctx.config.subtasks.length) throw new FormatError('Problem data not found.');
|
||||||
startPromise.then(() => ctx.next({ status: STATUS.STATUS_COMPILING }));
|
startPromise.then(() => ctx.next({ status: STATUS.STATUS_COMPILING }));
|
||||||
ctx.checker = await (() => {
|
ctx.checker = await (() => {
|
||||||
if (['default', 'strict'].includes(ctx.config.checker_type || 'default')) {
|
|
||||||
return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) };
|
|
||||||
}
|
|
||||||
const copyIn = { user_code: ctx.code };
|
const copyIn = { user_code: ctx.code };
|
||||||
for (const file of ctx.config.judge_extra_files) {
|
for (const file of ctx.config.judge_extra_files) {
|
||||||
copyIn[parseFilename(file)] = { src: file };
|
copyIn[parseFilename(file)] = { src: file };
|
||||||
|
|||||||
@@ -1,29 +1,9 @@
|
|||||||
import { format, inspect, InspectOptions } from 'util';
|
import { format, inspect, InspectOptions } from 'util';
|
||||||
import cac from 'cac';
|
import cac from 'cac';
|
||||||
|
import { Time } from '@hydrooj/utils/lib/utils';
|
||||||
|
|
||||||
const argv = cac().parse();
|
const argv = cac().parse();
|
||||||
|
|
||||||
namespace Time {
|
|
||||||
export const second = 1000;
|
|
||||||
export const minute = second * 60;
|
|
||||||
export const hour = minute * 60;
|
|
||||||
export const day = hour * 24;
|
|
||||||
export const week = day * 7;
|
|
||||||
export function formatTimeShort(ms: number) {
|
|
||||||
const abs = Math.abs(ms);
|
|
||||||
if (abs >= day - hour / 2) {
|
|
||||||
return `${Math.round(ms / day)}d`;
|
|
||||||
} if (abs >= hour - minute / 2) {
|
|
||||||
return `${Math.round(ms / hour)}h`;
|
|
||||||
} if (abs >= minute - second / 2) {
|
|
||||||
return `${Math.round(ms / minute)}m`;
|
|
||||||
} if (abs >= second) {
|
|
||||||
return `${Math.round(ms / second)}s`;
|
|
||||||
}
|
|
||||||
return `${ms}ms`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62,
|
20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62,
|
||||||
63, 68, 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113,
|
63, 68, 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import cac from 'cac';
|
import cac from 'cac';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
import PQueue from 'p-queue';
|
||||||
import { ParseEntry } from 'shell-quote';
|
import { ParseEntry } from 'shell-quote';
|
||||||
import { STATUS } from '@hydrooj/utils/lib/status';
|
import { STATUS } from '@hydrooj/utils/lib/status';
|
||||||
import { getConfig } from './config';
|
import { getConfig } from './config';
|
||||||
@@ -191,3 +192,9 @@ 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') });
|
||||||
|
|
||||||
|
export function runQueued(execute: string, params?: Parameter, priority = 0) {
|
||||||
|
return queue.add(() => run(execute, params), { priority });
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import { STATUS } from '@hydrooj/utils/lib/status';
|
|||||||
import type { JudgeResultBody } from 'hydrooj';
|
import type { JudgeResultBody } from 'hydrooj';
|
||||||
import { Logger } from 'hydrooj/src/logger';
|
import { Logger } from 'hydrooj/src/logger';
|
||||||
import * as monitor from 'hydrooj/src/service/monitor';
|
import * as monitor from 'hydrooj/src/service/monitor';
|
||||||
import readCases from './cases';
|
import readCases, { processTestdata } from './cases';
|
||||||
import { getConfig } from './config';
|
import { getConfig } from './config';
|
||||||
import { CompileError, FormatError, SystemError } from './error';
|
import { CompileError, FormatError, SystemError } from './error';
|
||||||
import { CopyInFile } from './sandbox/interface';
|
import { CopyInFile } from './sandbox/interface';
|
||||||
@@ -23,7 +23,6 @@ declare module 'hydrooj/src/interface' {
|
|||||||
'hydrojudge.cache_dir': string;
|
'hydrojudge.cache_dir': string;
|
||||||
'hydrojudge.tmp_dir': string;
|
'hydrojudge.tmp_dir': string;
|
||||||
'hydrojudge.tmpfs_size': string;
|
'hydrojudge.tmpfs_size': string;
|
||||||
'hydrojudge.retry_delay_sec': number;
|
|
||||||
'hydrojudge.sandbox_host': string;
|
'hydrojudge.sandbox_host': string;
|
||||||
'hydrojudge.memoryMax': string;
|
'hydrojudge.memoryMax': string;
|
||||||
'hydrojudge.testcases_max': number;
|
'hydrojudge.testcases_max': number;
|
||||||
@@ -54,28 +53,6 @@ async function postInit() {
|
|||||||
monitor.updateJudge({ mid, ...inf });
|
monitor.updateJudge({ mid, ...inf });
|
||||||
}, 1200000);
|
}, 1200000);
|
||||||
|
|
||||||
async function processData(folder: string) {
|
|
||||||
let files = await fs.readdir(folder);
|
|
||||||
let ini = false;
|
|
||||||
for (const i of files) {
|
|
||||||
if (i.toLowerCase() === 'config.ini') {
|
|
||||||
ini = true;
|
|
||||||
await fs.rename(`${folder}/${i}`, `${folder}/config.ini`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ini) {
|
|
||||||
for (const i of files) {
|
|
||||||
if (i.toLowerCase() === 'input') await fs.rename(`${folder}/${i}`, `${folder}/input`);
|
|
||||||
else if (i.toLowerCase() === 'output') await fs.rename(`${folder}/${i}`, `${folder}/output`);
|
|
||||||
}
|
|
||||||
files = await fs.readdir(`${folder}/input`);
|
|
||||||
for (const i of files) await fs.rename(`${folder}/input/${i}`, `${folder}/input/${i.toLowerCase()}`);
|
|
||||||
files = await fs.readdir(`${folder}/output`);
|
|
||||||
for (const i of files) await fs.rename(`${folder}/output/${i}`, `${folder}/output/${i.toLowerCase()}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchCodeFile(name: string) {
|
async function fetchCodeFile(name: string) {
|
||||||
const target = path.join('/tmp/hydro/judge', name.replace(/\//g, '_'));
|
const target = path.join('/tmp/hydro/judge', name.replace(/\//g, '_'));
|
||||||
await storage.get(`submission/${name}`, target);
|
await storage.get(`submission/${name}`, target);
|
||||||
@@ -104,7 +81,7 @@ async function postInit() {
|
|||||||
}
|
}
|
||||||
fs.writeFileSync(path.join(filePath, 'etags'), JSON.stringify(version));
|
fs.writeFileSync(path.join(filePath, 'etags'), JSON.stringify(version));
|
||||||
fs.writeFileSync(path.join(filePath, 'lastUsage'), new Date().getTime().toString());
|
fs.writeFileSync(path.join(filePath, 'lastUsage'), new Date().getTime().toString());
|
||||||
await processData(filePath).catch(noop);
|
await processTestdata(filePath);
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// TODO this should be moved into web
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { noop } from 'lodash';
|
import { noop } from 'lodash';
|
||||||
@@ -30,6 +31,8 @@ int main(){
|
|||||||
type: 'default',
|
type: 'default',
|
||||||
time: '3s',
|
time: '3s',
|
||||||
memory: '256m',
|
memory: '256m',
|
||||||
|
user_extra_files: [],
|
||||||
|
judge_extra_files: [],
|
||||||
},
|
},
|
||||||
stat: {},
|
stat: {},
|
||||||
clean: [],
|
clean: [],
|
||||||
|
|||||||
Reference in new issue
Block a user