Skip to content

Commit

Permalink
Implement submission memfd output
Browse files Browse the repository at this point in the history
  • Loading branch information
quantum5 committed Dec 31, 2024
1 parent 315ede5 commit d5a3a24
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
1 change: 1 addition & 0 deletions dmoj/graders/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def close(self) -> None:

class InteractiveGrader(StandardGrader):
check: CheckerOutput
memfd_output = False

def _launch_process(self, case, input_file=None):
super()._launch_process(case, input_file=None)
Expand Down
61 changes: 49 additions & 12 deletions dmoj/graders/standard.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import logging
import subprocess
from typing import Any

from dmoj.checkers import CheckerOutput
from dmoj.cptbox import TracedPopen
from dmoj.cptbox.lazy_bytes import LazyBytes
from dmoj.cptbox.utils import MemoryIO, MmapableIO
from dmoj.error import OutputLimitExceeded
from dmoj.executors import executors
from dmoj.executors.base_executor import BaseExecutor
from dmoj.graders.base import BaseGrader
from dmoj.problem import TestCase
from dmoj.judge import JudgeWorker
from dmoj.problem import Problem, TestCase
from dmoj.result import CheckerResult, Result

log = logging.getLogger('dmoj.graders')


class StandardGrader(BaseGrader):
_stdout_io: MmapableIO
_stderr_io: MmapableIO
_orig_fsize: int
memfd_output: bool = True

def __init__(self, judge: 'JudgeWorker', problem: Problem, language: str, source: bytes) -> None:
super().__init__(judge, problem, language, source)
self._orig_fsize = self.binary.fsize

def grade(self, case: TestCase) -> Result:
result = Result(case)

Expand Down Expand Up @@ -83,34 +95,59 @@ def check_result(self, case: TestCase, result: Result) -> CheckerOutput:
return check

def _launch_process(self, case: TestCase, input_file=None) -> None:
stdout: Any
stderr: Any

if self.memfd_output:
stdout = self._stdout_io = MemoryIO()
stderr = self._stderr_io = MemoryIO()
self.binary.fsize = max(self._orig_fsize, case.config.output_limit_length + 1024, 1048576)
else:
stdout = subprocess.PIPE
stderr = subprocess.PIPE

self._current_proc = self.binary.launch(
time=self.problem.time_limit,
memory=self.problem.memory_limit,
symlinks=case.config.symlinks,
stdin=input_file or subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=stdout,
stderr=stderr,
wall_time=case.config.wall_time_factor * self.problem.time_limit,
)

def _interact_with_process(self, case: TestCase, result: Result) -> bytes:
process = self._current_proc
assert process is not None
try:
result.proc_output, error = process.communicate(
None, outlimit=case.config.output_limit_length, errlimit=1048576
)
except OutputLimitExceeded:
error = b''
process.kill()
finally:

if self.memfd_output:
process.wait()

result.proc_output = self._stdout_io.to_bytes()
self._stdout_io.close()

if len(result.proc_output) > case.config.output_limit_length:
process.mark_ole()

error = self._stderr_io.to_bytes()
self._stderr_io.close()
else:
try:
result.proc_output, error = process.communicate(
None, outlimit=case.config.output_limit_length, errlimit=1048576
)
except OutputLimitExceeded:
error = b''
process.kill()
finally:
process.wait()
return error

def _generate_binary(self) -> BaseExecutor:
return executors[self.language].Executor(
executor = executors[self.language].Executor(
self.problem.id,
self.source,
hints=self.problem.config.hints or [],
unbuffered=self.problem.config.unbuffered,
)
return executor
2 changes: 1 addition & 1 deletion dmoj/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Result:
'OLE': 'yellow',
'IE': 'red',
}
CODE_DISPLAY_ORDER = ('IE', 'TLE', 'MLE', 'OLE', 'RTE', 'IR', 'WA', 'SC')
CODE_DISPLAY_ORDER = ('IE', 'OLE', 'TLE', 'MLE', 'RTE', 'IR', 'WA', 'SC')

def __init__(
self,
Expand Down

0 comments on commit d5a3a24

Please sign in to comment.