diff --git a/api/src/api/v2.js b/api/src/api/v2.js index 3a71306..22c74f2 100644 --- a/api/src/api/v2.js +++ b/api/src/api/v2.js @@ -61,6 +61,8 @@ function get_job(body) { run_memory_limit, run_timeout, compile_timeout, + run_cpu_time, + compile_cpu_time, } = body; return new Promise((resolve, reject) => { @@ -106,7 +108,7 @@ function get_job(body) { }); } - for (const constraint of ['memory_limit', 'timeout']) { + for (const constraint of ['memory_limit', 'timeout', 'cpu_time']) { for (const type of ['compile', 'run']) { const constraint_name = `${type}_${constraint}`; const constraint_value = body[constraint_name]; @@ -145,6 +147,10 @@ function get_job(body) { run: run_timeout ?? rt.timeouts.run, compile: compile_timeout ?? rt.timeouts.compile, }, + cpu_times: { + run: run_cpu_time ?? rt.cpu_times.run, + compile: compile_cpu_time ?? rt.cpu_times.compile, + }, memory_limits: { run: run_memory_limit ?? rt.memory_limits.run, compile: compile_memory_limit ?? rt.memory_limits.compile, @@ -272,6 +278,7 @@ router.post('/execute', async (req, res) => { try { job = await get_job(req.body); } catch (error) { + logger.error({ error }); return res.status(400).json(error); } try { diff --git a/api/src/config.js b/api/src/config.js index b8fa97d..034e3b6 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -90,6 +90,18 @@ const options = { parser: parse_int, validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], }, + compile_cpu_time: { + desc: 'Max CPU time allowed for compile stage in milliseconds', + default: 10000, // 10 seconds + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, + run_cpu_time: { + desc: 'Max CPU time allowed for run stage in milliseconds', + default: 3000, // 3 seconds + parser: parse_int, + validators: [(x, raw) => !is_nan(x) || `${raw} is not a number`], + }, compile_memory_limit: { desc: 'Max memory usage for compile stage in bytes (set to -1 for no limit)', default: -1, // no limit @@ -117,7 +129,7 @@ const options = { limit_overrides: { desc: 'Per-language exceptions in JSON format for each of:\ max_process_count, max_open_files, max_file_size, compile_memory_limit,\ - run_memory_limit, compile_timeout, run_timeout, output_max_size', + run_memory_limit, compile_timeout, run_timeout, compile_cpu_time, run_cpu_time, output_max_size', default: {}, parser: parse_overrides, validators: [ @@ -165,6 +177,8 @@ function parse_overrides(overrides_string) { 'run_memory_limit', 'compile_timeout', 'run_timeout', + 'compile_cpu_time', + 'run_cpu_time', 'output_max_size', ].includes(key) ) { diff --git a/api/src/job.js b/api/src/job.js index 189d73b..f8d0043 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -22,7 +22,15 @@ const get_next_box_id = () => ++box_id % MAX_BOX_ID; class Job { #dirty_boxes; - constructor({ runtime, files, args, stdin, timeouts, memory_limits }) { + constructor({ + runtime, + files, + args, + stdin, + timeouts, + cpu_times, + memory_limits, + }) { this.uuid = uuidv4(); this.logger = logplease.create(`job/${this.uuid}`); @@ -44,6 +52,7 @@ class Job { } this.timeouts = timeouts; + this.cpu_times = cpu_times; this.memory_limits = memory_limits; this.state = job_states.READY; @@ -116,7 +125,15 @@ class Job { return box; } - async safe_call(box, file, args, timeout, memory_limit, event_bus = null) { + async safe_call( + box, + file, + args, + timeout, + cpu_time, + memory_limit, + event_bus = null + ) { let stdout = ''; let stderr = ''; let output = ''; @@ -125,7 +142,8 @@ class Job { let signal = null; let message = null; let status = null; - let time = null; + let cpu_time_stat = null; + let wall_time_stat = null; const proc = cp.spawn( ISOLATE_PATH, @@ -143,7 +161,8 @@ class Job { `--processes=${this.runtime.max_process_count}`, `--open-files=${this.runtime.max_open_files}`, `--fsize=${Math.floor(this.runtime.max_file_size / 1000)}`, - `--time=${timeout / 1000}`, + `--wall-time=${timeout / 1000}`, + `--time=${cpu_time / 1000}`, `--extra-time=0`, ...(memory_limit >= 0 ? [`--cg-mem=${Math.floor(memory_limit / 1000)}`] @@ -292,13 +311,22 @@ class Job { break; case 'time': try { - time = parse_float(value); + cpu_time_stat = parse_float(value); } catch (e) { throw new Error( `Failed to parse cpu time, received value: ${value}` ); } break; + case 'time-wall': + try { + wall_time_stat = parse_float(value); + } catch (e) { + throw new Error( + `Failed to parse wall time, received value: ${value}` + ); + } + break; default: break; } @@ -317,7 +345,8 @@ class Job { memory, message, status, - time, + cpu_time: cpu_time_stat, + wall_time: wall_time_stat, }; } @@ -367,6 +396,7 @@ class Job { '/runtime/compile', code_files.map(x => x.name), this.timeouts.compile, + this.cpu_times.compile, this.memory_limits.compile, event_bus ); @@ -391,6 +421,7 @@ class Job { '/runtime/run', [code_files[0].name, ...this.args], this.timeouts.run, + this.cpu_times.run, this.memory_limits.run, event_bus ); diff --git a/api/src/runtime.js b/api/src/runtime.js index 6c6f10e..9a2adf4 100644 --- a/api/src/runtime.js +++ b/api/src/runtime.js @@ -15,6 +15,7 @@ class Runtime { pkgdir, runtime, timeouts, + cpu_times, memory_limits, max_process_count, max_open_files, @@ -27,6 +28,7 @@ class Runtime { this.pkgdir = pkgdir; this.runtime = runtime; this.timeouts = timeouts; + this.cpu_times = cpu_times; this.memory_limits = memory_limits; this.max_process_count = max_process_count; this.max_open_files = max_open_files; @@ -62,6 +64,18 @@ class Runtime { language_limit_overrides ), }, + cpu_times: { + compile: this.compute_single_limit( + language_name, + 'compile_cpu_time', + language_limit_overrides + ), + run: this.compute_single_limit( + language_name, + 'run_cpu_time', + language_limit_overrides + ), + }, memory_limits: { compile: this.compute_single_limit( language_name, diff --git a/docs/configuration.md b/docs/configuration.md index 1a6f5bd..4c4aa55 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -178,7 +178,7 @@ default: {} ``` Per-language overrides/exceptions for the each of `max_process_count`, `max_open_files`, `max_file_size`, -`compile_memory_limit`, `run_memory_limit`, `compile_timeout`, `run_timeout`, `output_max_size`. Defined as follows: +`compile_memory_limit`, `run_memory_limit`, `compile_timeout`, `run_timeout`, `compile_cpu_time`, `run_cpu_time`, `output_max_size`. Defined as follows: ``` PISTON_LIMIT_OVERRIDES={"c++":{"max_process_count":128}} diff --git a/packages/zig/0.10.1/metadata.json b/packages/zig/0.10.1/metadata.json index 9ecb955..20a9963 100644 --- a/packages/zig/0.10.1/metadata.json +++ b/packages/zig/0.10.1/metadata.json @@ -3,6 +3,7 @@ "version": "0.10.1", "aliases": [], "limit_overrides": { - "compile_timeout": 15000 + "compile_timeout": 15000, + "compile_cpu_time": 15000 } -} \ No newline at end of file +} diff --git a/packages/zig/0.8.0/metadata.json b/packages/zig/0.8.0/metadata.json index 8c02d33..38bc1fc 100644 --- a/packages/zig/0.8.0/metadata.json +++ b/packages/zig/0.8.0/metadata.json @@ -3,6 +3,7 @@ "version": "0.8.0", "aliases": ["zig"], "limit_overrides": { - "compile_timeout": 15000 + "compile_timeout": 15000, + "compile_cpu_time": 15000 } } diff --git a/packages/zig/0.9.1/metadata.json b/packages/zig/0.9.1/metadata.json index e7061cd..1ad7a70 100644 --- a/packages/zig/0.9.1/metadata.json +++ b/packages/zig/0.9.1/metadata.json @@ -3,6 +3,7 @@ "version": "0.9.1", "aliases": ["zig"], "limit_overrides": { - "compile_timeout": 15000 + "compile_timeout": 15000, + "compile_cpu_time": 15000 } }