@@ -27,6 +27,29 @@ def __init__(self, message, interactor_out, env, renv, irenv):
27
27
)
28
28
29
29
30
+ class Pipes :
31
+ """
32
+ Class for storing file descriptors for interactor and solution processes.
33
+ """
34
+ r_interactor = None
35
+ w_interactor = None
36
+ r_solution = None
37
+ w_solution = None
38
+
39
+ def __init__ (self , r_interactor , w_interactor , r_solution , w_solution ):
40
+ """
41
+ Constructor for Pipes class.
42
+ :param r_interactor: file descriptor from which the interactor reads from the solution
43
+ :param w_interactor: file descriptor to which the interactor writes to the solution
44
+ :param r_solution: file descriptor from which the solution reads from the interactor
45
+ :param w_solution: file descriptor to which the solution writes to the interactor
46
+ """
47
+ self .r_interactor = r_interactor
48
+ self .w_interactor = w_interactor
49
+ self .r_solution = r_solution
50
+ self .w_solution = w_solution
51
+
52
+
30
53
def _limit_length (s ):
31
54
if len (s ) > RESULT_STRING_LENGTH_LIMIT :
32
55
suffix = b'[...]'
@@ -70,18 +93,7 @@ def _fill_result(env, renv, irenv, interactor_out):
70
93
inter_sig = irenv .get ('exit_signal' , None )
71
94
sigpipe = signal .SIGPIPE .value
72
95
73
- if irenv ['result_code' ] != 'OK' and inter_sig != sigpipe :
74
- renv ['result_code' ] = 'SE'
75
- raise InteractorError (f'Interactor got { irenv ["result_code" ]} .' , interactor_out , env , renv , irenv )
76
- elif renv ['result_code' ] != 'OK' and sol_sig != sigpipe :
77
- return
78
- elif len (interactor_out ) == 0 :
79
- renv ['result_code' ] = 'SE'
80
- raise InteractorError (f'Empty interactor out.' , interactor_out , env , renv , irenv )
81
- elif inter_sig == sigpipe :
82
- renv ['result_code' ] = 'WA'
83
- renv ['result_string' ] = 'solution exited prematurely'
84
- else :
96
+ if six .ensure_binary (interactor_out [0 ]) != b'' :
85
97
renv ['result_string' ] = ''
86
98
if six .ensure_binary (interactor_out [0 ]) == b'OK' :
87
99
renv ['result_code' ] = 'OK'
@@ -93,11 +105,25 @@ def _fill_result(env, renv, irenv, interactor_out):
93
105
if interactor_out [1 ]:
94
106
renv ['result_string' ] = _limit_length (interactor_out [1 ])
95
107
renv ['result_percentage' ] = (0 , 1 )
108
+ elif irenv ['result_code' ] != 'OK' and irenv ['result_code' ] != 'TLE' and inter_sig != sigpipe :
109
+ renv ['result_code' ] = 'SE'
110
+ raise InteractorError (f'Interactor got { irenv ["result_code" ]} .' , interactor_out , env , renv , irenv )
111
+ elif renv ['result_code' ] != 'OK' and sol_sig != sigpipe :
112
+ return
113
+ elif inter_sig == sigpipe :
114
+ renv ['result_code' ] = 'WA'
115
+ renv ['result_string' ] = 'solution exited prematurely'
116
+ elif irenv .get ('real_time_killed' , False ):
117
+ renv ['result_code' ] = 'TLE'
118
+ renv ['result_string' ] = 'interactor time limit exceeded (user\' s solution or interactor can be the cause)'
119
+ else :
120
+ raise InteractorError (f'Unexpected interactor error' , interactor_out , env , renv , irenv )
96
121
97
122
98
123
def _run (environ , executor , use_sandboxes ):
99
124
input_name = tempcwd ('in' )
100
125
126
+ num_processes = environ .get ('num_processes' , 1 )
101
127
file_executor = get_file_runner (executor , environ )
102
128
interactor_executor = DetailedUnprotectedExecutor ()
103
129
exe_filename = file_executor .preferred_filename ()
@@ -113,13 +139,18 @@ def _run(environ, executor, use_sandboxes):
113
139
os .mkdir (zipdir )
114
140
try :
115
141
input_name = _extract_input_if_zipfile (input_name , zipdir )
142
+ proc_pipes = []
116
143
117
- r1 , w1 = os .pipe ()
118
- r2 , w2 = os .pipe ()
119
- for fd in (r1 , w1 , r2 , w2 ):
120
- os .set_inheritable (fd , True )
144
+ for i in range (num_processes ):
145
+ r1 , w1 = os .pipe ()
146
+ r2 , w2 = os .pipe ()
147
+ for fd in (r1 , w1 , r2 , w2 ):
148
+ os .set_inheritable (fd , True )
149
+ proc_pipes .append (Pipes (r1 , w2 , r2 , w1 ))
121
150
122
- interactor_args = [os .path .basename (input_name ), 'out' ]
151
+ interactor_args = [str (num_processes )]
152
+ for pipes in proc_pipes :
153
+ interactor_args .extend ([str (pipes .r_interactor ), str (pipes .w_interactor )])
123
154
124
155
interactor_time_limit = 2 * environ ['exec_time_limit' ]
125
156
@@ -139,51 +170,68 @@ def run(self):
139
170
except Exception as e :
140
171
self .exception = e
141
172
142
- with interactor_executor as ie :
143
- interactor = ExecutionWrapper (
144
- ie ,
145
- [tempcwd (interactor_filename )] + interactor_args ,
146
- stdin = r2 ,
147
- stdout = w1 ,
148
- ignore_errors = True ,
149
- environ = environ ,
150
- environ_prefix = 'interactor_' ,
151
- mem_limit = DEFAULT_INTERACTOR_MEM_LIMIT ,
152
- time_limit = interactor_time_limit ,
153
- fds_to_close = (r2 , w1 ),
154
- close_passed_fd = True ,
155
- cwd = tempcwd (),
156
- in_file = environ ['in_file' ],
157
- )
158
-
159
- with file_executor as fe :
160
- exe = ExecutionWrapper (
161
- fe ,
162
- tempcwd (exe_filename ),
163
- [],
164
- stdin = r1 ,
165
- stdout = w2 ,
166
- ignore_errors = True ,
167
- environ = environ ,
168
- environ_prefix = 'exec_' ,
169
- fds_to_close = (r1 , w2 ),
170
- close_passed_fd = True ,
171
- cwd = tempcwd (),
172
- in_file = environ ['in_file' ],
173
- )
174
-
175
- exe .start ()
176
- interactor .start ()
177
-
178
- exe .join ()
179
- interactor .join ()
180
-
181
- for ew in (exe , interactor ):
182
- if ew .exception is not None :
183
- raise ew .exception
184
-
185
- renv = exe .value
186
- irenv = interactor .value
173
+ with open (input_name , 'rb' ) as infile , open (tempcwd ('out' ), 'wb' ) as outfile :
174
+ processes = []
175
+ interactor_fds = []
176
+ for pipes in proc_pipes :
177
+ interactor_fds .extend ([pipes .r_interactor , pipes .w_interactor ])
178
+
179
+ with interactor_executor as ie :
180
+ interactor = ExecutionWrapper (
181
+ ie ,
182
+ [tempcwd (interactor_filename )] + interactor_args ,
183
+ stdin = infile ,
184
+ stdout = outfile ,
185
+ ignore_errors = True ,
186
+ environ = environ ,
187
+ environ_prefix = 'interactor_' ,
188
+ mem_limit = DEFAULT_INTERACTOR_MEM_LIMIT ,
189
+ time_limit = interactor_time_limit ,
190
+ fds_to_close = interactor_fds ,
191
+ pass_fds = interactor_fds ,
192
+ cwd = tempcwd (),
193
+ )
194
+
195
+ for i in range (num_processes ):
196
+ pipes = proc_pipes [i ]
197
+ with file_executor as fe :
198
+ exe = ExecutionWrapper (
199
+ fe ,
200
+ tempcwd (exe_filename ),
201
+ [str (i )],
202
+ stdin = pipes .r_solution ,
203
+ stdout = pipes .w_solution ,
204
+ ignore_errors = True ,
205
+ environ = environ ,
206
+ environ_prefix = 'exec_' ,
207
+ fds_to_close = [pipes .r_solution , pipes .w_solution ],
208
+ cwd = tempcwd (),
209
+ )
210
+ processes .append (exe )
211
+
212
+ for process in processes :
213
+ process .start ()
214
+ interactor .start ()
215
+
216
+ for process in processes :
217
+ process .join ()
218
+ interactor .join ()
219
+
220
+ if interactor .exception :
221
+ raise interactor .exception
222
+ for process in processes :
223
+ if process .exception :
224
+ raise process .exception
225
+
226
+ renv = processes [0 ].value
227
+ for process in processes :
228
+ if process .value ['result_code' ] != 'OK' :
229
+ renv = process .value
230
+ break
231
+ renv ['time_used' ] = max (renv ['time_used' ], process .value ['time_used' ])
232
+ renv ['mem_used' ] = max (renv ['mem_used' ], process .value ['mem_used' ])
233
+
234
+ irenv = interactor .value
187
235
188
236
try :
189
237
with open (tempcwd ('out' ), 'rb' ) as result_file :
0 commit comments