|
4 | 4 | from dmoj.checkers import CheckerOutput |
5 | 5 | from dmoj.cptbox import TracedPopen |
6 | 6 | from dmoj.cptbox.lazy_bytes import LazyBytes |
| 7 | +from dmoj.cptbox.utils import MemoryIO, MmapableIO |
7 | 8 | from dmoj.error import OutputLimitExceeded |
8 | 9 | from dmoj.executors import executors |
9 | 10 | from dmoj.executors.base_executor import BaseExecutor |
|
15 | 16 |
|
16 | 17 |
|
17 | 18 | class StandardGrader(BaseGrader): |
| 19 | + _stdout_io: MmapableIO |
| 20 | + _stderr_io: MmapableIO |
| 21 | + _orig_fsize: int |
| 22 | + memfd_output: bool = True |
| 23 | + |
18 | 24 | def grade(self, case: TestCase) -> Result: |
19 | 25 | result = Result(case) |
20 | 26 |
|
@@ -83,34 +89,54 @@ def check_result(self, case: TestCase, result: Result) -> CheckerOutput: |
83 | 89 | return check |
84 | 90 |
|
85 | 91 | 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 | + |
86 | 100 | self._current_proc = self.binary.launch( |
87 | 101 | time=self.problem.time_limit, |
88 | 102 | memory=self.problem.memory_limit, |
89 | 103 | symlinks=case.config.symlinks, |
90 | 104 | stdin=input_file or subprocess.PIPE, |
91 | | - stdout=subprocess.PIPE, |
92 | | - stderr=subprocess.PIPE, |
| 105 | + stdout=stdout, |
| 106 | + stderr=stderr, |
93 | 107 | wall_time=case.config.wall_time_factor * self.problem.time_limit, |
94 | 108 | ) |
95 | 109 |
|
96 | 110 | def _interact_with_process(self, case: TestCase, result: Result) -> bytes: |
97 | 111 | process = self._current_proc |
98 | 112 | 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: |
107 | 115 | 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() |
108 | 132 | return error |
109 | 133 |
|
110 | 134 | def _generate_binary(self) -> BaseExecutor: |
111 | | - return executors[self.language].Executor( |
| 135 | + executor = executors[self.language].Executor( |
112 | 136 | self.problem.id, |
113 | 137 | self.source, |
114 | 138 | hints=self.problem.config.hints or [], |
115 | 139 | unbuffered=self.problem.config.unbuffered, |
116 | 140 | ) |
| 141 | + self._orig_fsize = executor.fsize |
| 142 | + return executor |
0 commit comments