@@ -33,7 +33,7 @@ def __init__(self) -> None:
33
33
self ._initialized = False
34
34
self ._cmd_lock = threading .Lock ()
35
35
self ._fetch_lock = threading .Lock ()
36
- self ._proc = None
36
+ self ._procs = []
37
37
self ._stop = False
38
38
self ._cwd = None
39
39
self ._env = None
@@ -85,18 +85,23 @@ def _inner_stop(self, sig: int, timeout: float = 30) -> None:
85
85
86
86
self ._stop = True
87
87
88
- if self ._proc :
89
- self ._logger .info ("Terminating process with %s" , sig )
90
- self ._proc .send_signal (sig )
91
-
92
88
with Timeout (timeout ) as timer :
93
- while self ._fetch_lock .locked ():
94
- time .sleep (1e-6 )
95
- timer .check (err_msg = "Timeout waiting for command to stop" )
89
+ if self ._procs :
90
+ self ._logger .info (
91
+ "Terminating %d process(es) with %s" ,
92
+ len (self ._procs ), sig )
93
+
94
+ for proc in self ._procs :
95
+ proc .send_signal (sig )
96
+
97
+ while proc .poll () is None :
98
+ time .sleep (1e-6 )
99
+ timer .check (
100
+ err_msg = "Timeout waiting for command to stop" )
96
101
97
- while self ._cmd_lock .locked ():
102
+ while self ._fetch_lock .locked ():
98
103
time .sleep (1e-6 )
99
- timer .check (err_msg = "Timeout waiting for command to stop " )
104
+ timer .check (err_msg = "Timeout waiting to fetch file " )
100
105
101
106
self ._logger .info ("Process terminated" )
102
107
@@ -114,14 +119,18 @@ def force_stop(
114
119
iobuffer : IOBuffer = None ) -> None :
115
120
self ._inner_stop (signal .SIGKILL , timeout )
116
121
117
- def _read_stdout (self , size : int , iobuffer : IOBuffer = None ) -> str :
122
+ def _read_stdout (
123
+ self ,
124
+ proc : subprocess .Popen ,
125
+ size : int ,
126
+ iobuffer : IOBuffer = None ) -> str :
118
127
"""
119
128
Read data from stdout.
120
129
"""
121
130
if not self .is_running :
122
131
return None
123
132
124
- data = os .read (self . _proc .stdout .fileno (), size )
133
+ data = os .read (proc .stdout .fileno (), size )
125
134
rdata = data .decode (encoding = "utf-8" , errors = "replace" )
126
135
rdata = rdata .replace ('\r ' , '' )
127
136
@@ -143,82 +152,82 @@ def run_command(self,
143
152
if not self .is_running :
144
153
raise SUTError ("SUT is not running" )
145
154
146
- with self ._cmd_lock :
147
- t_secs = max (timeout , 0 )
148
-
149
- self ._logger .info ("Executing command (timeout=%d): %s" ,
150
- t_secs ,
151
- command )
152
-
153
- # pylint: disable=consider-using-with
154
- self ._proc = subprocess .Popen (
155
- command ,
156
- stdout = subprocess .PIPE ,
157
- stderr = subprocess .STDOUT ,
158
- cwd = self ._cwd ,
159
- env = self ._env ,
160
- shell = True )
161
-
162
- ret = None
163
- t_start = time .time ()
164
- t_end = 0
165
- stdout = ""
155
+ t_secs = max (timeout , 0 )
156
+
157
+ self ._logger .info (
158
+ "Executing command (timeout=%.3f): %s" ,
159
+ t_secs ,
160
+ command )
161
+
162
+ # pylint: disable=consider-using-with
163
+ proc = subprocess .Popen (
164
+ command ,
165
+ stdout = subprocess .PIPE ,
166
+ stderr = subprocess .STDOUT ,
167
+ cwd = self ._cwd ,
168
+ env = self ._env ,
169
+ shell = True )
170
+
171
+ self ._procs .append (proc )
172
+
173
+ ret = None
174
+ t_start = time .time ()
175
+ t_end = 0
176
+ stdout = ""
177
+
178
+ try :
179
+ poller = select .epoll ()
180
+ poller .register (
181
+ proc .stdout .fileno (),
182
+ select .POLLIN |
183
+ select .POLLPRI |
184
+ select .POLLHUP |
185
+ select .POLLERR )
186
+
187
+ with Timeout (timeout ) as timer :
188
+ while True :
189
+ events = poller .poll (0.1 )
190
+ for fdesc , _ in events :
191
+ if fdesc != proc .stdout .fileno ():
192
+ break
166
193
167
- try :
168
- poller = select .epoll ()
169
- poller .register (
170
- self ._proc .stdout .fileno (),
171
- select .POLLIN |
172
- select .POLLPRI |
173
- select .POLLHUP |
174
- select .POLLERR )
194
+ data = self ._read_stdout (proc , 1024 , iobuffer )
195
+ if data :
196
+ stdout += data
175
197
176
- with Timeout (timeout ) as timer :
177
- while True :
178
- events = poller .poll (0.1 )
179
- for fdesc , _ in events :
180
- if fdesc != self ._proc .stdout .fileno ():
181
- break
182
-
183
- data = self ._read_stdout (1024 , iobuffer )
184
- if data :
185
- stdout += data
198
+ if proc .poll () is not None :
199
+ break
186
200
187
- if self ._proc .poll () is not None :
188
- break
201
+ timer .check (
202
+ err_msg = "Timeout during command execution" ,
203
+ exc = SUTTimeoutError )
189
204
190
- timer .check (
191
- err_msg = "Timeout during command execution" ,
192
- exc = SUTTimeoutError )
205
+ t_end = time .time () - t_start
193
206
194
- t_end = time .time () - t_start
207
+ # once the process stopped, we still might have some data
208
+ # inside the stdout buffer
209
+ while not self ._stop :
210
+ data = self ._read_stdout (proc , 1024 , iobuffer )
211
+ if not data :
212
+ break
195
213
196
- # once the process stopped, we still might have some data
197
- # inside the stdout buffer
198
- while not self ._stop :
199
- data = self ._read_stdout (1024 , iobuffer )
200
- if not data :
201
- break
214
+ stdout += data
215
+ finally :
216
+ self ._procs .remove (proc )
202
217
203
- stdout += data
204
- except subprocess .TimeoutExpired as err :
205
- self ._proc .kill ()
206
- raise SUTError (err )
207
- finally :
208
- ret = {
209
- "command" : command ,
210
- "stdout" : stdout ,
211
- "returncode" : self ._proc .returncode ,
212
- "timeout" : t_secs ,
213
- "exec_time" : t_end ,
214
- }
218
+ ret = {
219
+ "command" : command ,
220
+ "stdout" : stdout ,
221
+ "returncode" : proc .returncode ,
222
+ "timeout" : t_secs ,
223
+ "exec_time" : t_end ,
224
+ }
215
225
216
- self ._logger .debug ("return data=%s" , ret )
217
- self ._proc = None
226
+ self ._logger .debug ("return data=%s" , ret )
218
227
219
- self ._logger .info ("Command executed" )
228
+ self ._logger .info ("Command executed" )
220
229
221
- return ret
230
+ return ret
222
231
223
232
def fetch_file (
224
233
self ,
0 commit comments