Skip to content

Implement submission memfd output #1198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 3 additions & 1 deletion dmoj/graders/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ def _generate_binary(self) -> BaseExecutor:

aux_sources[handler_data['header']] = header
entry = entry_point
return executors[self.language].Executor(
executor = executors[self.language].Executor(
self.problem.id, entry, aux_sources=aux_sources, defines=['-DSIGNATURE_GRADER']
)
self._orig_fsize = executor.fsize
return executor
else:
raise InternalError('no valid runtime for signature grading %s found' % self.language)
55 changes: 44 additions & 11 deletions dmoj/graders/standard.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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
Expand All @@ -15,6 +17,11 @@


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

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

Expand Down Expand Up @@ -83,34 +90,60 @@ 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,
)
self._orig_fsize = executor.fsize
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
Loading