Skip to content

Commit 2d1fc88

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

File tree

4 files changed

+49
-13
lines changed

4 files changed

+49
-13
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: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import logging
22
import subprocess
3+
from typing import Any
34

45
from dmoj.checkers import CheckerOutput
56
from dmoj.cptbox import TracedPopen
67
from dmoj.cptbox.lazy_bytes import LazyBytes
8+
from dmoj.cptbox.utils import MemoryIO, MmapableIO
79
from dmoj.error import OutputLimitExceeded
810
from dmoj.executors import executors
911
from dmoj.executors.base_executor import BaseExecutor
@@ -15,6 +17,11 @@
1517

1618

1719
class StandardGrader(BaseGrader):
20+
_stdout_io: MmapableIO
21+
_stderr_io: MmapableIO
22+
_orig_fsize: int
23+
memfd_output: bool = True
24+
1825
def grade(self, case: TestCase) -> Result:
1926
result = Result(case)
2027

@@ -83,34 +90,60 @@ def check_result(self, case: TestCase, result: Result) -> CheckerOutput:
8390
return check
8491

8592
def _launch_process(self, case: TestCase, input_file=None) -> None:
93+
stdout: Any
94+
stderr: Any
95+
96+
if self.memfd_output:
97+
stdout = self._stdout_io = MemoryIO()
98+
stderr = self._stderr_io = MemoryIO()
99+
self.binary.fsize = max(self._orig_fsize, case.config.output_limit_length + 1024, 1048576)
100+
else:
101+
stdout = subprocess.PIPE
102+
stderr = subprocess.PIPE
103+
86104
self._current_proc = self.binary.launch(
87105
time=self.problem.time_limit,
88106
memory=self.problem.memory_limit,
89107
symlinks=case.config.symlinks,
90108
stdin=input_file or subprocess.PIPE,
91-
stdout=subprocess.PIPE,
92-
stderr=subprocess.PIPE,
109+
stdout=stdout,
110+
stderr=stderr,
93111
wall_time=case.config.wall_time_factor * self.problem.time_limit,
94112
)
95113

96114
def _interact_with_process(self, case: TestCase, result: Result) -> bytes:
97115
process = self._current_proc
98116
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:
117+
118+
if self.memfd_output:
107119
process.wait()
120+
121+
result.proc_output = self._stdout_io.to_bytes()
122+
self._stdout_io.close()
123+
124+
if len(result.proc_output) > case.config.output_limit_length:
125+
process.mark_ole()
126+
127+
error = self._stderr_io.to_bytes()
128+
self._stderr_io.close()
129+
else:
130+
try:
131+
result.proc_output, error = process.communicate(
132+
None, outlimit=case.config.output_limit_length, errlimit=1048576
133+
)
134+
except OutputLimitExceeded:
135+
error = b''
136+
process.kill()
137+
finally:
138+
process.wait()
108139
return error
109140

110141
def _generate_binary(self) -> BaseExecutor:
111-
return executors[self.language].Executor(
142+
executor = executors[self.language].Executor(
112143
self.problem.id,
113144
self.source,
114145
hints=self.problem.config.hints or [],
115146
unbuffered=self.problem.config.unbuffered,
116147
)
148+
self._orig_fsize = executor.fsize
149+
return executor

dmoj/result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Result:
3131
'OLE': 'yellow',
3232
'IE': 'red',
3333
}
34-
CODE_DISPLAY_ORDER = ('IE', 'TLE', 'MLE', 'OLE', 'RTE', 'IR', 'WA', 'SC')
34+
CODE_DISPLAY_ORDER = ('IE', 'OLE', 'TLE', 'MLE', 'RTE', 'IR', 'WA', 'SC')
3535

3636
def __init__(
3737
self,

0 commit comments

Comments
 (0)