rbx.cp 0.5.72__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rbx/annotations.py +21 -1
- rbx/box/cli.py +24 -8
- rbx/box/code.py +140 -3
- rbx/box/contest/build_contest_statements.py +44 -34
- rbx/box/contest/contest_utils.py +25 -0
- rbx/box/contest/main.py +24 -0
- rbx/box/contest/schema.py +52 -8
- rbx/box/contest/statements.py +53 -25
- rbx/box/download.py +19 -1
- rbx/box/fields.py +35 -0
- rbx/box/lang.py +27 -0
- rbx/box/package.py +1 -1
- rbx/box/packaging/boca/packager.py +48 -5
- rbx/box/packaging/contest_main.py +13 -0
- rbx/box/packaging/main.py +13 -2
- rbx/box/packaging/packager.py +4 -4
- rbx/box/packaging/pkg/packager.py +142 -0
- rbx/box/packaging/polygon/packager.py +2 -24
- rbx/box/packaging/polygon/upload.py +35 -17
- rbx/box/remote.py +2 -2
- rbx/box/schema.py +68 -18
- rbx/box/solutions.py +6 -1
- rbx/box/statements/build_statements.py +44 -27
- rbx/box/statements/builders.py +18 -10
- rbx/box/statements/expander.py +49 -0
- rbx/box/statements/latex_jinja.py +61 -4
- rbx/box/statements/schema.py +33 -9
- rbx/box/testcase_utils.py +19 -47
- rbx/box/tooling/__init__.py +0 -0
- rbx/box/tooling/boca/__init__.py +0 -0
- rbx/box/tooling/boca/main.py +13 -0
- rbx/box/tooling/boca/scrape.py +34 -0
- rbx/box/{packaging/boca/upload.py → tooling/boca/scraper.py} +77 -8
- rbx/box/tooling/main.py +8 -0
- rbx/box/ui/screens/run_explorer.py +1 -1
- rbx/box/ui/widgets/interaction_box.py +19 -1
- rbx/grading/caching.py +18 -2
- rbx/grading/judge/sandbox.py +48 -5
- rbx/grading/judge/sandboxes/isolate.py +1 -0
- rbx/grading/judge/sandboxes/stupid_sandbox.py +11 -5
- rbx/grading/judge/sandboxes/timeit.py +36 -15
- rbx/grading/processing_context.py +62 -78
- rbx/grading/steps.py +91 -40
- rbx/resources/packagers/boca/checker.sh +4 -1
- rbx/resources/packagers/boca/compile/c +2 -6
- rbx/resources/packagers/boca/compile/cc +2 -6
- rbx/resources/packagers/boca/compile/cpp +2 -6
- rbx/resources/packagers/boca/compile/java +1 -6
- rbx/resources/packagers/boca/compile/kt +24 -28
- rbx/resources/packagers/boca/compile/py2 +2 -6
- rbx/resources/packagers/boca/compile/py3 +2 -6
- rbx/resources/packagers/boca/interactive/c +15 -62
- rbx/resources/packagers/boca/interactive/cc +15 -62
- rbx/resources/packagers/boca/interactive/cpp +15 -61
- rbx/resources/packagers/boca/interactive/java +15 -67
- rbx/resources/packagers/boca/interactive/kt +15 -67
- rbx/resources/packagers/boca/interactive/py2 +15 -67
- rbx/resources/packagers/boca/interactive/py3 +15 -65
- rbx/resources/packagers/boca/interactor_compile.sh +5 -2
- rbx/resources/packagers/boca/interactor_run.sh +174 -0
- rbx/resources/packagers/boca/safeexec.c +530 -0
- rbx/resources/packagers/boca/safeexec_compile.sh +49 -0
- rbx/resources/presets/default/contest/contest.rbx.yml +9 -8
- rbx/resources/presets/default/problem/problem.rbx.yml +27 -26
- rbx/resources/templates/rbx.h +2 -3
- {rbx_cp-0.5.72.dist-info → rbx_cp-0.6.0.dist-info}/METADATA +2 -1
- {rbx_cp-0.5.72.dist-info → rbx_cp-0.6.0.dist-info}/RECORD +70 -59
- rbx/resources/packagers/boca/compile/pas +0 -172
- {rbx_cp-0.5.72.dist-info → rbx_cp-0.6.0.dist-info}/LICENSE +0 -0
- {rbx_cp-0.5.72.dist-info → rbx_cp-0.6.0.dist-info}/WHEEL +0 -0
- {rbx_cp-0.5.72.dist-info → rbx_cp-0.6.0.dist-info}/entry_points.txt +0 -0
@@ -5,6 +5,7 @@ import resource
|
|
5
5
|
import signal
|
6
6
|
import stat
|
7
7
|
import sys
|
8
|
+
import threading
|
8
9
|
from time import monotonic
|
9
10
|
from typing import Any, Dict, List, Optional, Set, Union
|
10
11
|
|
@@ -25,6 +26,7 @@ class Options:
|
|
25
26
|
file_duplicates: Dict[int, List[str]] = dataclasses.field(default_factory=dict)
|
26
27
|
prefixed: Set[str] = dataclasses.field(default_factory=set)
|
27
28
|
prefix: str = ''
|
29
|
+
process_group: Optional[int] = None
|
28
30
|
|
29
31
|
|
30
32
|
def exit_with(code: int):
|
@@ -91,17 +93,16 @@ def create_tee(files, mode, buffer_size=4096, prefix=''):
|
|
91
93
|
# Close parent's end of the pipe
|
92
94
|
os.close(pipe_write)
|
93
95
|
|
94
|
-
|
95
|
-
while bytes:
|
96
|
+
new = True
|
97
|
+
while bytes := os.read(pipe_read, 1):
|
96
98
|
for tee in tee_list:
|
97
|
-
if tee.prefix:
|
99
|
+
if tee.prefix and new:
|
98
100
|
tee.file.write(tee.prefix)
|
99
101
|
tee.file.write(bytes)
|
100
102
|
tee.file.flush()
|
103
|
+
new = bytes == b'\n'
|
101
104
|
# TODO maybe add in fsync() here if the fileno() method
|
102
105
|
# exists on file
|
103
|
-
|
104
|
-
bytes = os.read(pipe_read, buffer_size)
|
105
106
|
except Exception:
|
106
107
|
pass
|
107
108
|
finally:
|
@@ -150,6 +151,8 @@ def parse_opts() -> Options:
|
|
150
151
|
options.fs_limit = int(opt[2:])
|
151
152
|
elif opt.startswith('-P'):
|
152
153
|
options.prefix = opt[2:]
|
154
|
+
elif opt.startswith('-g'):
|
155
|
+
options.process_group = int(opt[2:])
|
153
156
|
else:
|
154
157
|
raise Exception(f'Invalid option {opt}')
|
155
158
|
num_opts += 1
|
@@ -270,6 +273,7 @@ def wait_and_finish(
|
|
270
273
|
entries.append(f'time-wall: {wall_time:.3f}')
|
271
274
|
entries.append(f'mem: {memory_used}')
|
272
275
|
entries.append(f'file: {file_sizes}')
|
276
|
+
entries.append(f'pid: {pid}')
|
273
277
|
|
274
278
|
output_file = pathlib.Path(sys.argv[1])
|
275
279
|
output_file.parent.mkdir(parents=True, exist_ok=True)
|
@@ -279,6 +283,9 @@ def wait_and_finish(
|
|
279
283
|
def main():
|
280
284
|
options = parse_opts()
|
281
285
|
|
286
|
+
if options.process_group is not None:
|
287
|
+
os.setpgid(0, options.process_group)
|
288
|
+
|
282
289
|
start_time = monotonic()
|
283
290
|
sub_pid = os.fork()
|
284
291
|
if sub_pid == 0:
|
@@ -292,13 +299,21 @@ def main():
|
|
292
299
|
alarm_msg: List[Optional[str]] = [None]
|
293
300
|
status_holder: Set[str] = set()
|
294
301
|
|
295
|
-
|
302
|
+
stop_wall_handler = threading.Event()
|
303
|
+
stop_alarm_handler = threading.Event()
|
304
|
+
|
305
|
+
def handle_wall():
|
306
|
+
if stop_wall_handler.wait(options.wall_time_limit):
|
307
|
+
return
|
308
|
+
stop_alarm_handler.set()
|
296
309
|
nonlocal alarm_msg
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
310
|
+
alarm_msg[0] = 'wall timelimit'
|
311
|
+
os.kill(sub_pid, 9)
|
312
|
+
|
313
|
+
def handle_alarm():
|
314
|
+
if stop_alarm_handler.wait(0.3):
|
301
315
|
return
|
316
|
+
nonlocal alarm_msg
|
302
317
|
ru = resource.getrusage(resource.RUSAGE_CHILDREN)
|
303
318
|
if options.time_limit is not None:
|
304
319
|
cpu_time = get_cpu_time(ru)
|
@@ -313,20 +328,26 @@ def main():
|
|
313
328
|
os.kill(sub_pid, 9)
|
314
329
|
return
|
315
330
|
|
316
|
-
|
331
|
+
stop_alarm_handler.clear()
|
332
|
+
handle_alarm()
|
333
|
+
|
334
|
+
alarm_handler = threading.Thread(target=handle_alarm, daemon=True)
|
335
|
+
wall_handler = threading.Thread(target=handle_wall, daemon=True)
|
336
|
+
alarm_handler.start()
|
337
|
+
wall_handler.start()
|
317
338
|
|
318
339
|
def handle_sub_term(*args, **kwargs):
|
319
340
|
nonlocal status_holder
|
320
341
|
status_holder.add('TE')
|
321
342
|
os.kill(sub_pid, 9)
|
322
343
|
|
323
|
-
signal.setitimer(signal.ITIMER_REAL, 0.3)
|
324
|
-
signal.signal(signal.SIGALRM, handle_alarm)
|
325
344
|
signal.signal(signal.SIGTERM, handle_sub_term)
|
326
345
|
|
327
346
|
wait_and_finish(sub_pid, options, start_time, status_holder, alarm_msg=alarm_msg)
|
328
|
-
|
329
|
-
|
347
|
+
|
348
|
+
# Process finished, stop the handlers.
|
349
|
+
stop_wall_handler.set()
|
350
|
+
stop_alarm_handler.set()
|
330
351
|
|
331
352
|
# Exit gracefully.
|
332
353
|
sys.exit(0)
|
@@ -1,87 +1,71 @@
|
|
1
|
-
import asyncio
|
2
1
|
import contextlib
|
3
2
|
import os
|
4
3
|
import signal
|
5
|
-
import
|
6
|
-
from typing import
|
4
|
+
import subprocess
|
5
|
+
from typing import List, Optional
|
7
6
|
|
8
|
-
|
9
|
-
_terminate_all_on_error = False
|
10
|
-
_lock = threading.Lock()
|
11
|
-
|
12
|
-
# Creating a processing context is not thread-safe, but adding to it is.
|
7
|
+
from rbx.grading.judge.sandbox import SandboxBase
|
13
8
|
|
14
9
|
|
15
10
|
@contextlib.contextmanager
|
16
|
-
def
|
17
|
-
|
18
|
-
with _lock:
|
19
|
-
old_processing_context_pids = _processing_context_pids
|
20
|
-
_old_terminate_all_on_error = _terminate_all_on_error
|
21
|
-
_processing_context_pids = set()
|
22
|
-
_terminate_all_on_error = terminate_all_on_error
|
11
|
+
def new_process_group():
|
12
|
+
p = subprocess.Popen(['/bin/bash', '-c', 'exec sleep infinity'])
|
23
13
|
try:
|
24
|
-
yield
|
14
|
+
yield p.pid
|
25
15
|
finally:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
except ChildProcessError:
|
83
|
-
return
|
84
|
-
if pid in wait_pids:
|
85
|
-
process(pid, os.waitstatus_to_exitcode(status))
|
86
|
-
|
87
|
-
await asyncio.to_thread(wait_all_processes)
|
16
|
+
p.terminate()
|
17
|
+
p.wait()
|
18
|
+
|
19
|
+
|
20
|
+
def should_use_group(sandboxes: List[SandboxBase]) -> bool:
|
21
|
+
if not sandboxes:
|
22
|
+
return False
|
23
|
+
uses_pgid = all(sandbox.use_pgid() for sandbox in sandboxes)
|
24
|
+
all_pgids = set(
|
25
|
+
sandbox.params.pgid for sandbox in sandboxes if sandbox.params.pgid is not None
|
26
|
+
)
|
27
|
+
return uses_pgid and len(all_pgids) == 1
|
28
|
+
|
29
|
+
|
30
|
+
async def _fetch_pids(sandboxes: List[SandboxBase]) -> List[int]:
|
31
|
+
return [await sandbox.get_pid() for sandbox in sandboxes]
|
32
|
+
|
33
|
+
|
34
|
+
def _find_sandbox_idx(pids: List[int], pid: int) -> Optional[int]:
|
35
|
+
try:
|
36
|
+
return pids.index(pid)
|
37
|
+
except ValueError:
|
38
|
+
return None
|
39
|
+
|
40
|
+
|
41
|
+
async def _wait_for_group(sandboxes: List[SandboxBase]) -> List[int]:
|
42
|
+
pgid = [
|
43
|
+
sandbox.params.pgid for sandbox in sandboxes if sandbox.params.pgid is not None
|
44
|
+
][0]
|
45
|
+
assert pgid is not None
|
46
|
+
|
47
|
+
sandbox_pids = await _fetch_pids(sandboxes)
|
48
|
+
|
49
|
+
finished = []
|
50
|
+
while len(finished) < len(sandboxes):
|
51
|
+
try:
|
52
|
+
pid, status = os.waitpid(-pgid, 0)
|
53
|
+
except ChildProcessError:
|
54
|
+
break
|
55
|
+
|
56
|
+
if os.waitstatus_to_exitcode(status) != 0:
|
57
|
+
os.kill(pgid, signal.SIGKILL)
|
58
|
+
|
59
|
+
sandbox_idx = _find_sandbox_idx(sandbox_pids, pid)
|
60
|
+
if sandbox_idx is not None:
|
61
|
+
finished.append(sandbox_idx)
|
62
|
+
continue
|
63
|
+
|
64
|
+
return finished
|
65
|
+
|
66
|
+
|
67
|
+
async def wait_all(sandboxes: List[SandboxBase]):
|
68
|
+
if not should_use_group(sandboxes):
|
69
|
+
raise RuntimeError('Sandboxes are not using a process group')
|
70
|
+
|
71
|
+
await _wait_for_group(sandboxes)
|
rbx/grading/steps.py
CHANGED
@@ -10,7 +10,6 @@ import shutil
|
|
10
10
|
import subprocess
|
11
11
|
import sys
|
12
12
|
import tempfile
|
13
|
-
import typing
|
14
13
|
from enum import Enum
|
15
14
|
from typing import IO, Any, Dict, Iterable, List, Optional, Tuple, Union
|
16
15
|
|
@@ -53,6 +52,27 @@ class Outcome(Enum):
|
|
53
52
|
Outcome.IDLENESS_LIMIT_EXCEEDED,
|
54
53
|
]
|
55
54
|
|
55
|
+
def short_name(self) -> str:
|
56
|
+
if self == Outcome.ACCEPTED:
|
57
|
+
return 'AC'
|
58
|
+
if self == Outcome.WRONG_ANSWER:
|
59
|
+
return 'WA'
|
60
|
+
if self == Outcome.TIME_LIMIT_EXCEEDED:
|
61
|
+
return 'TLE'
|
62
|
+
if self == Outcome.IDLENESS_LIMIT_EXCEEDED:
|
63
|
+
return 'ILE'
|
64
|
+
if self == Outcome.MEMORY_LIMIT_EXCEEDED:
|
65
|
+
return 'MLE'
|
66
|
+
if self == Outcome.RUNTIME_ERROR:
|
67
|
+
return 'RTE'
|
68
|
+
if self == Outcome.OUTPUT_LIMIT_EXCEEDED:
|
69
|
+
return 'OLE'
|
70
|
+
if self == Outcome.JUDGE_FAILED:
|
71
|
+
return 'FL'
|
72
|
+
if self == Outcome.INTERNAL_ERROR:
|
73
|
+
return 'IE'
|
74
|
+
return 'XX'
|
75
|
+
|
56
76
|
|
57
77
|
class DigestHolder(BaseModel):
|
58
78
|
value: Optional[str] = None
|
@@ -372,12 +392,12 @@ def _is_c_command(exe_command: str) -> bool:
|
|
372
392
|
return 'gcc' in exe_command or 'clang' in exe_command
|
373
393
|
|
374
394
|
|
375
|
-
def
|
395
|
+
def is_cpp_command(exe_command: str) -> bool:
|
376
396
|
return 'g++' in exe_command or 'clang++' in exe_command
|
377
397
|
|
378
398
|
|
379
399
|
def is_cxx_command(exe_command: str) -> bool:
|
380
|
-
return
|
400
|
+
return is_cpp_command(exe_command) or _is_c_command(exe_command)
|
381
401
|
|
382
402
|
|
383
403
|
def is_cxx_sanitizer_command(command: str) -> bool:
|
@@ -413,7 +433,8 @@ def _complain_about_clang() -> None:
|
|
413
433
|
)
|
414
434
|
|
415
435
|
|
416
|
-
|
436
|
+
@functools.cache
|
437
|
+
def _get_cxx_version_output(command: str, extra_flags: str = '') -> Optional[str]:
|
417
438
|
cmds = shlex.split(command)
|
418
439
|
if not cmds:
|
419
440
|
return None
|
@@ -421,8 +442,8 @@ def _get_cxx_version_output(command: str) -> Optional[str]:
|
|
421
442
|
if not is_cxx_command(exe):
|
422
443
|
return None
|
423
444
|
|
424
|
-
|
425
|
-
output = subprocess.run([exe, '-v'], capture_output=True)
|
445
|
+
extra = shlex.split(extra_flags)
|
446
|
+
output = subprocess.run([exe, '-v', *extra], capture_output=True, input='')
|
426
447
|
if output.returncode != 0:
|
427
448
|
console.print('[error]Failed to get C/C++ compiler version.[/error]')
|
428
449
|
return None
|
@@ -430,6 +451,8 @@ def _get_cxx_version_output(command: str) -> Optional[str]:
|
|
430
451
|
|
431
452
|
|
432
453
|
def _maybe_get_bits_stdcpp_for_clang(command: str) -> Optional[GradingFileInput]:
|
454
|
+
if not is_cpp_command(get_exe_from_command(command)):
|
455
|
+
return None
|
433
456
|
version_output = _get_cxx_version_output(command)
|
434
457
|
if version_output is None:
|
435
458
|
return None
|
@@ -446,11 +469,44 @@ def _maybe_get_bits_stdcpp_for_clang(command: str) -> Optional[GradingFileInput]
|
|
446
469
|
return GradingFileInput(src=bits, dest=pathlib.Path('bits/stdc++.h'))
|
447
470
|
|
448
471
|
|
449
|
-
def
|
472
|
+
def _find_system_paths_in_version_output(version_output: str) -> List[pathlib.Path]:
|
473
|
+
res = []
|
474
|
+
start = False
|
475
|
+
for line in version_output.splitlines():
|
476
|
+
if line.startswith('#include <...> search starts here:'):
|
477
|
+
start = True
|
478
|
+
continue
|
479
|
+
if not start:
|
480
|
+
continue
|
481
|
+
if not line.startswith(' '):
|
482
|
+
break
|
483
|
+
res.append(pathlib.Path(line.strip()))
|
484
|
+
return res
|
485
|
+
|
486
|
+
|
487
|
+
def _get_system_bits_stdcpp(command: str) -> Optional[GradingFileInput]:
|
488
|
+
if not is_cpp_command(get_exe_from_command(command)):
|
489
|
+
return None
|
490
|
+
version_output = _get_cxx_version_output(command, '-xc++ -E -')
|
491
|
+
if version_output is None:
|
492
|
+
return None
|
493
|
+
for path in _find_system_paths_in_version_output(version_output):
|
494
|
+
bits_candidate = path / 'bits' / 'stdc++.h'
|
495
|
+
if not bits_candidate.is_file():
|
496
|
+
continue
|
497
|
+
return GradingFileInput(
|
498
|
+
src=bits_candidate.resolve().absolute(), dest=pathlib.Path('bits/stdc++.h')
|
499
|
+
)
|
500
|
+
return None
|
501
|
+
|
502
|
+
|
503
|
+
def maybe_get_bits_stdcpp_for_commands(
|
450
504
|
commands: List[str],
|
451
505
|
) -> Optional[GradingFileInput]:
|
452
506
|
for command in commands:
|
453
|
-
res =
|
507
|
+
res = _get_system_bits_stdcpp(command) or _maybe_get_bits_stdcpp_for_clang(
|
508
|
+
command
|
509
|
+
)
|
454
510
|
if res is not None:
|
455
511
|
return res
|
456
512
|
return None
|
@@ -560,10 +616,9 @@ def compile(
|
|
560
616
|
sandbox: SandboxBase,
|
561
617
|
artifacts: GradingArtifacts,
|
562
618
|
) -> bool:
|
619
|
+
sandbox.reset()
|
620
|
+
|
563
621
|
commands = _try_following_alias_for_commands(commands)
|
564
|
-
bits_artifact = _maybe_get_bits_stdcpp_for_commands(commands)
|
565
|
-
if bits_artifact is not None:
|
566
|
-
_process_input_artifacts(GradingArtifacts(inputs=[bits_artifact]), sandbox)
|
567
622
|
_process_input_artifacts(artifacts, sandbox)
|
568
623
|
|
569
624
|
if not commands:
|
@@ -571,7 +626,9 @@ def compile(
|
|
571
626
|
return True
|
572
627
|
|
573
628
|
logs: List[PreprocessLog] = []
|
574
|
-
sandbox.set_params(
|
629
|
+
sandbox.set_params(
|
630
|
+
params.model_copy(deep=True)
|
631
|
+
) # Copy to allow further modification.
|
575
632
|
|
576
633
|
for i, command in enumerate(commands):
|
577
634
|
_maybe_complain_about_sanitization(command)
|
@@ -584,10 +641,6 @@ def compile(
|
|
584
641
|
if is_java_like_command(get_exe_from_command(command)):
|
585
642
|
sandbox.params.address_space = None
|
586
643
|
|
587
|
-
if bits_artifact is not None and _is_cpp_command(cmd[0]):
|
588
|
-
# Include from sandbox directory to import bits/stdc++.h.
|
589
|
-
cmd.append('-I.')
|
590
|
-
|
591
644
|
if not sandbox.execute_without_std(cmd):
|
592
645
|
console.print(
|
593
646
|
'[error]Sandbox crashed while processing command:[/error]',
|
@@ -642,12 +695,15 @@ async def run(
|
|
642
695
|
sandbox: SandboxBase,
|
643
696
|
artifacts: GradingArtifacts,
|
644
697
|
metadata: Optional[RunLogMetadata] = None,
|
645
|
-
kill_on_processing_context_exit: bool = False,
|
646
698
|
) -> Optional[RunLog]:
|
699
|
+
sandbox.reset()
|
700
|
+
|
647
701
|
_process_input_artifacts(artifacts, sandbox)
|
648
702
|
_process_fifos(artifacts, sandbox)
|
649
703
|
cmd = _split_and_expand(command, sandbox)
|
650
|
-
sandbox.set_params(
|
704
|
+
sandbox.set_params(
|
705
|
+
params.model_copy(deep=True)
|
706
|
+
) # Copy to allow further modification.
|
651
707
|
|
652
708
|
# Remove memory constraints for Java.
|
653
709
|
if is_java_like_command(get_exe_from_command(command)):
|
@@ -662,9 +718,6 @@ async def run(
|
|
662
718
|
)
|
663
719
|
return None
|
664
720
|
|
665
|
-
if sandbox.get_exit_code() != 0 and kill_on_processing_context_exit:
|
666
|
-
processing_context.terminate_all_processes_in_context(clear=False)
|
667
|
-
|
668
721
|
if not _process_output_artifacts(artifacts, sandbox):
|
669
722
|
return None
|
670
723
|
|
@@ -706,27 +759,25 @@ async def run_coordinated(
|
|
706
759
|
interactor: CoordinatedRunParams,
|
707
760
|
solution: CoordinatedRunParams,
|
708
761
|
) -> Tuple[Optional[RunLog], Optional[RunLog]]:
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
params.artifacts,
|
718
|
-
params.metadata,
|
719
|
-
kill_on_processing_context_exit=True,
|
720
|
-
)
|
762
|
+
def run_one(params: CoordinatedRunParams) -> asyncio.Task[Optional[RunLog]]:
|
763
|
+
return asyncio.create_task(
|
764
|
+
run(
|
765
|
+
params.command,
|
766
|
+
params.params,
|
767
|
+
params.sandbox,
|
768
|
+
params.artifacts,
|
769
|
+
params.metadata,
|
721
770
|
)
|
722
|
-
for params in [interactor, solution]
|
723
|
-
)
|
724
|
-
await processing_context.wait_all_processes_in_context(wait_for=2)
|
725
|
-
logs = typing.cast(
|
726
|
-
Tuple[Optional[RunLog], Optional[RunLog]],
|
727
|
-
tuple(await asyncio.gather(*runs)),
|
728
771
|
)
|
729
|
-
|
772
|
+
|
773
|
+
# Use interactor PID as the process group id.
|
774
|
+
interactor_task = run_one(interactor)
|
775
|
+
solution.sandbox.params.pgid = await interactor.sandbox.get_pid()
|
776
|
+
solution_task = run_one(solution)
|
777
|
+
|
778
|
+
await processing_context.wait_all([interactor.sandbox, solution.sandbox])
|
779
|
+
|
780
|
+
return await asyncio.gather(interactor_task, solution_task)
|
730
781
|
|
731
782
|
|
732
783
|
def _normalize_checked_words(s: str) -> Tuple[str, ...]:
|
@@ -1,4 +1,7 @@
|
|
1
1
|
### START OF CHECKER COMPILATION
|
2
|
+
CACHE_DIR="/tmp/boca-cache"
|
3
|
+
mkdir -p $CACHE_DIR
|
4
|
+
|
2
5
|
CDIR=$(pwd)
|
3
6
|
CHECKER_PATH="checker.cpp"
|
4
7
|
CHECKER_OUT="checker.exe"
|
@@ -27,7 +30,7 @@ printf "%s" "${RbxHeaderContent}" >rbx.h
|
|
27
30
|
printf "%s" "${CheckerContent}" >$CHECKER_PATH
|
28
31
|
|
29
32
|
checker_hash=($(cat $CHECKER_PATH rbx.h testlib.h | md5sum))
|
30
|
-
checker_cache="/
|
33
|
+
checker_cache="$CACHE_DIR/checker-${checker_hash}"
|
31
34
|
|
32
35
|
echo "Polygon checker hash: $checker_hash"
|
33
36
|
echo "Copying polygon checker to $CDIR/$CHECKER_OUT"
|
@@ -92,12 +92,8 @@ if [ "$4" != "" ]; then
|
|
92
92
|
fi
|
93
93
|
|
94
94
|
# setting up the timelimit according to the problem
|
95
|
-
|
96
|
-
|
97
|
-
else
|
98
|
-
time=$3
|
99
|
-
fi
|
100
|
-
let "ttime = $time + 30"
|
95
|
+
time=15
|
96
|
+
let "ttime = $time * 2"
|
101
97
|
|
102
98
|
if [ "$2" == "" ]; then
|
103
99
|
exe=run.exe
|
@@ -93,12 +93,8 @@ if [ "$4" != "" ]; then
|
|
93
93
|
fi
|
94
94
|
|
95
95
|
# setting up the timelimit according to the problem
|
96
|
-
|
97
|
-
|
98
|
-
else
|
99
|
-
time=$3
|
100
|
-
fi
|
101
|
-
let "ttime = $time + 30"
|
96
|
+
time=15
|
97
|
+
let "ttime = $time * 2"
|
102
98
|
|
103
99
|
if [ "$2" == "" ]; then
|
104
100
|
exe=run.exe
|
@@ -92,12 +92,8 @@ if [ "$4" != "" ]; then
|
|
92
92
|
fi
|
93
93
|
|
94
94
|
# setting up the timelimit according to the problem
|
95
|
-
|
96
|
-
|
97
|
-
else
|
98
|
-
time=$3
|
99
|
-
fi
|
100
|
-
let "ttime = $time + 30"
|
95
|
+
time=15
|
96
|
+
let "ttime = $time * 2"
|
101
97
|
|
102
98
|
if [ "$2" == "" ]; then
|
103
99
|
exe=run.exe
|
@@ -88,13 +88,8 @@ if [ ! -x $sf ]; then
|
|
88
88
|
fi
|
89
89
|
|
90
90
|
# setting up the timelimit according to the problem
|
91
|
-
if [ "$3" == "" ]; then
|
92
|
-
time=5
|
93
|
-
else
|
94
|
-
time=$3
|
95
|
-
fi
|
96
|
-
let "ttime = $time + 30"
|
97
91
|
time=30
|
92
|
+
ttime=30
|
98
93
|
|
99
94
|
maxm=512
|
100
95
|
if [ "$4" != "" -a "$4" -gt "512" ]; then
|