@@ -141,18 +141,30 @@ def returncode_to_str(code):
141
141
return f'returned { code } '
142
142
143
143
144
- # Runs multiple subprocess commands.
145
- # bool 'check': If True (default), raises an exception if any of the subprocesses failed with a nonzero exit code.
146
- # string 'route_stdout_to_temp_files_suffix': if not None, all stdouts are instead written to files, and an array of filenames is returned.
147
- # bool 'pipe_stdout': If True, an array of stdouts is returned, for each subprocess.
148
144
def run_multiple_processes (commands ,
149
145
env = None ,
150
146
route_stdout_to_temp_files_suffix = None ,
151
147
pipe_stdout = False ,
152
148
check = True ,
153
149
cwd = None ):
150
+ """Runs multiple subprocess commands.
151
+
152
+ check : bool
153
+ If True (default), raises an exception if any of the subprocesses
154
+ failed with a nonzero exit code.
155
+
156
+ route_stdout_to_temp_files_suffix : string
157
+ if not None, all stdouts are instead written to files, and an array
158
+ of filenames is returned.
159
+
160
+ pipe_stdout : bool
161
+ If True, an array of stdouts is returned, for each subprocess.
162
+ """
163
+ assert not (route_stdout_to_temp_files_suffix and pipe_stdout ), 'Cannot simultaneously pipe stdout to file and a string! Choose one or the other.'
164
+
154
165
if env is None :
155
166
env = os .environ .copy ()
167
+
156
168
# By default, avoid using Python multiprocessing library due to a large amount of bugs it has on Windows (#8013, #718, #13785, etc.)
157
169
# Use EM_PYTHON_MULTIPROCESSING=1 environment variable to enable it. It can be faster, but may not work on Windows.
158
170
if int (os .getenv ('EM_PYTHON_MULTIPROCESSING' , '0' )):
@@ -164,65 +176,61 @@ def run_multiple_processes(commands,
164
176
165
177
std_outs = []
166
178
167
- if route_stdout_to_temp_files_suffix and pipe_stdout :
168
- raise Exception ('Cannot simultaneously pipe stdout to file and a string! Choose one or the other.' )
169
-
170
179
# TODO: Experiment with registering a signal handler here to see if that helps with Ctrl-C locking up the command prompt
171
180
# when multiple child processes have been spawned.
172
181
# import signal
173
182
# def signal_handler(sig, frame):
174
183
# sys.exit(1)
175
184
# signal.signal(signal.SIGINT, signal_handler)
176
185
177
- with ToolchainProfiler .profile_block ('run_multiple_processes' ):
178
- processes = []
179
- num_parallel_processes = get_num_cores ()
180
- temp_files = get_temp_files ()
181
- i = 0
182
- num_completed = 0
183
-
184
- while num_completed < len (commands ):
185
- if i < len (commands ) and len (processes ) < num_parallel_processes :
186
- # Not enough parallel processes running, spawn a new one.
187
- std_out = temp_files .get (route_stdout_to_temp_files_suffix ) if route_stdout_to_temp_files_suffix else (subprocess .PIPE if pipe_stdout else None )
188
- if DEBUG :
189
- logger .debug ('Running subprocess %d/%d: %s' % (i + 1 , len (commands ), ' ' .join (commands [i ])))
190
- print_compiler_stage (commands [i ])
191
- processes += [(i , subprocess .Popen (commands [i ], stdout = std_out , stderr = subprocess .PIPE if pipe_stdout else None , env = env , cwd = cwd ))]
192
- if route_stdout_to_temp_files_suffix :
193
- std_outs += [(i , std_out .name )]
194
- i += 1
195
- else :
196
- # Not spawning a new process (Too many commands running in parallel, or no commands left): find if a process has finished.
197
- def get_finished_process ():
198
- while True :
199
- j = 0
200
- while j < len (processes ):
201
- if processes [j ][1 ].poll () is not None :
202
- out , err = processes [j ][1 ].communicate ()
203
- return (j , out .decode ('UTF-8' ) if out else '' , err .decode ('UTF-8' ) if err else '' )
204
- j += 1
205
- # All processes still running; wait a short while for the first (oldest) process to finish,
206
- # then look again if any process has completed.
207
- try :
208
- out , err = processes [0 ][1 ].communicate (0.2 )
209
- return (0 , out .decode ('UTF-8' ) if out else '' , err .decode ('UTF-8' ) if err else '' )
210
- except subprocess .TimeoutExpired :
211
- pass
212
-
213
- j , out , err = get_finished_process ()
214
- idx , finished_process = processes [j ]
215
- del processes [j ]
216
- if pipe_stdout :
217
- std_outs += [(idx , out )]
218
- if check and finished_process .returncode != 0 :
219
- if out :
220
- logger .info (out )
221
- if err :
222
- logger .error (err )
223
-
224
- raise Exception ('Subprocess %d/%d failed (%s)! (cmdline: %s)' % (idx + 1 , len (commands ), returncode_to_str (finished_process .returncode ), shlex_join (commands [idx ])))
225
- num_completed += 1
186
+ processes = []
187
+ num_parallel_processes = get_num_cores ()
188
+ temp_files = get_temp_files ()
189
+ i = 0
190
+ num_completed = 0
191
+
192
+ while num_completed < len (commands ):
193
+ if i < len (commands ) and len (processes ) < num_parallel_processes :
194
+ # Not enough parallel processes running, spawn a new one.
195
+ std_out = temp_files .get (route_stdout_to_temp_files_suffix ) if route_stdout_to_temp_files_suffix else (subprocess .PIPE if pipe_stdout else None )
196
+ if DEBUG :
197
+ logger .debug ('Running subprocess %d/%d: %s' % (i + 1 , len (commands ), ' ' .join (commands [i ])))
198
+ print_compiler_stage (commands [i ])
199
+ processes += [(i , subprocess .Popen (commands [i ], stdout = std_out , stderr = subprocess .PIPE if pipe_stdout else None , env = env , cwd = cwd ))]
200
+ if route_stdout_to_temp_files_suffix :
201
+ std_outs += [(i , std_out .name )]
202
+ i += 1
203
+ else :
204
+ # Not spawning a new process (Too many commands running in parallel, or no commands left): find if a process has finished.
205
+ def get_finished_process ():
206
+ while True :
207
+ j = 0
208
+ while j < len (processes ):
209
+ if processes [j ][1 ].poll () is not None :
210
+ out , err = processes [j ][1 ].communicate ()
211
+ return (j , out .decode ('UTF-8' ) if out else '' , err .decode ('UTF-8' ) if err else '' )
212
+ j += 1
213
+ # All processes still running; wait a short while for the first (oldest) process to finish,
214
+ # then look again if any process has completed.
215
+ try :
216
+ out , err = processes [0 ][1 ].communicate (0.2 )
217
+ return (0 , out .decode ('UTF-8' ) if out else '' , err .decode ('UTF-8' ) if err else '' )
218
+ except subprocess .TimeoutExpired :
219
+ pass
220
+
221
+ j , out , err = get_finished_process ()
222
+ idx , finished_process = processes [j ]
223
+ del processes [j ]
224
+ if pipe_stdout :
225
+ std_outs += [(idx , out )]
226
+ if check and finished_process .returncode != 0 :
227
+ if out :
228
+ logger .info (out )
229
+ if err :
230
+ logger .error (err )
231
+
232
+ raise Exception ('Subprocess %d/%d failed (%s)! (cmdline: %s)' % (idx + 1 , len (commands ), returncode_to_str (finished_process .returncode ), shlex_join (commands [idx ])))
233
+ num_completed += 1
226
234
227
235
# If processes finished out of order, sort the results to the order of the input.
228
236
std_outs .sort (key = lambda x : x [0 ])
0 commit comments