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 30, 2024
1 parent 315ede5 commit e7175fb
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 12 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
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)
48 changes: 37 additions & 11 deletions dmoj/graders/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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 +16,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 +89,54 @@ def check_result(self, case: TestCase, result: Result) -> CheckerOutput:
return check

def _launch_process(self, case: TestCase, input_file=None) -> None:
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()

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

0 comments on commit e7175fb

Please sign in to comment.