Skip to content

Commit e7175fb

Browse files
committed
Implement submission memfd output
1 parent 315ede5 commit e7175fb

File tree

3 files changed

+41
-12
lines changed

3 files changed

+41
-12
lines changed

dmoj/graders/interactive.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def close(self) -> None:
104104

105105
class InteractiveGrader(StandardGrader):
106106
check: CheckerOutput
107+
memfd_output = False
107108

108109
def _launch_process(self, case, input_file=None):
109110
super()._launch_process(case, input_file=None)

dmoj/graders/signature.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ def _generate_binary(self) -> BaseExecutor:
2626

2727
aux_sources[handler_data['header']] = header
2828
entry = entry_point
29-
return executors[self.language].Executor(
29+
executor = executors[self.language].Executor(
3030
self.problem.id, entry, aux_sources=aux_sources, defines=['-DSIGNATURE_GRADER']
3131
)
32+
self._orig_fsize = executor.fsize
33+
return executor
3234
else:
3335
raise InternalError('no valid runtime for signature grading %s found' % self.language)

dmoj/graders/standard.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from dmoj.checkers import CheckerOutput
55
from dmoj.cptbox import TracedPopen
66
from dmoj.cptbox.lazy_bytes import LazyBytes
7+
from dmoj.cptbox.utils import MemoryIO, MmapableIO
78
from dmoj.error import OutputLimitExceeded
89
from dmoj.executors import executors
910
from dmoj.executors.base_executor import BaseExecutor
@@ -15,6 +16,11 @@
1516

1617

1718
class StandardGrader(BaseGrader):
19+
_stdout_io: MmapableIO
20+
_stderr_io: MmapableIO
21+
_orig_fsize: int
22+
memfd_output: bool = True
23+
1824
def grade(self, case: TestCase) -> Result:
1925
result = Result(case)
2026

@@ -83,34 +89,54 @@ def check_result(self, case: TestCase, result: Result) -> CheckerOutput:
8389
return check
8490

8591
def _launch_process(self, case: TestCase, input_file=None) -> None:
92+
if self.memfd_output:
93+
stdout = self._stdout_io = MemoryIO()
94+
stderr = self._stderr_io = MemoryIO()
95+
self.binary.fsize = max(self._orig_fsize, case.config.output_limit_length + 1024, 1048576)
96+
else:
97+
stdout = subprocess.PIPE
98+
stderr = subprocess.PIPE
99+
86100
self._current_proc = self.binary.launch(
87101
time=self.problem.time_limit,
88102
memory=self.problem.memory_limit,
89103
symlinks=case.config.symlinks,
90104
stdin=input_file or subprocess.PIPE,
91-
stdout=subprocess.PIPE,
92-
stderr=subprocess.PIPE,
105+
stdout=stdout,
106+
stderr=stderr,
93107
wall_time=case.config.wall_time_factor * self.problem.time_limit,
94108
)
95109

96110
def _interact_with_process(self, case: TestCase, result: Result) -> bytes:
97111
process = self._current_proc
98112
assert process is not None
99-
try:
100-
result.proc_output, error = process.communicate(
101-
None, outlimit=case.config.output_limit_length, errlimit=1048576
102-
)
103-
except OutputLimitExceeded:
104-
error = b''
105-
process.kill()
106-
finally:
113+
114+
if self.memfd_output:
107115
process.wait()
116+
117+
result.proc_output = self._stdout_io.to_bytes()
118+
self._stdout_io.close()
119+
120+
error = self._stderr_io.to_bytes()
121+
self._stderr_io.close()
122+
else:
123+
try:
124+
result.proc_output, error = process.communicate(
125+
None, outlimit=case.config.output_limit_length, errlimit=1048576
126+
)
127+
except OutputLimitExceeded:
128+
error = b''
129+
process.kill()
130+
finally:
131+
process.wait()
108132
return error
109133

110134
def _generate_binary(self) -> BaseExecutor:
111-
return executors[self.language].Executor(
135+
executor = executors[self.language].Executor(
112136
self.problem.id,
113137
self.source,
114138
hints=self.problem.config.hints or [],
115139
unbuffered=self.problem.config.unbuffered,
116140
)
141+
self._orig_fsize = executor.fsize
142+
return executor

0 commit comments

Comments
 (0)