Skip to content

Commit 64006f5

Browse files
author
Vladimir Kotal
authored
wait for output thread to finish reading (#2126)
1 parent 6b4b1ac commit 64006f5

File tree

1 file changed

+23
-9
lines changed

1 file changed

+23
-9
lines changed

tools/sync/command.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,24 +113,27 @@ class OutputThread(threading.Thread):
113113
stdout/stderr buffers fill up.
114114
"""
115115

116-
def __init__(self):
116+
def __init__(self, event, logger):
117117
super(OutputThread, self).__init__()
118118
self.read_fd, self.write_fd = os.pipe()
119119
self.pipe_fobj = os.fdopen(self.read_fd)
120120
self.out = []
121+
self.event = event
122+
self.logger = logger
121123
self.start()
122124

123125
def run(self):
124126
"""
125127
It might happen that after the process is gone, the thread
126-
still has data to read from the pipe. Should probably introduce
127-
a boolean and set it to True under the 'if not line' block
128-
below and make the caller wait for it to become True.
128+
still has data to read from the pipe. Hence, event is used
129+
to synchronize with the caller.
129130
"""
130131
while True:
131132
line = self.pipe_fobj.readline()
132133
if not line:
134+
self.logger.debug("end of output")
133135
self.pipe_fobj.close()
136+
self.event.set()
134137
return
135138

136139
self.out.append(line)
@@ -142,6 +145,7 @@ def fileno(self):
142145
return self.write_fd
143146

144147
def close(self):
148+
self.logger.debug("closed")
145149
os.close(self.write_fd)
146150

147151
orig_work_dir = None
@@ -163,7 +167,8 @@ def close(self):
163167
return
164168

165169
timeout_thread = None
166-
output_thread = OutputThread()
170+
event = threading.Event()
171+
output_thread = OutputThread(event, self.logger)
167172
try:
168173
start_time = time.time()
169174
try:
@@ -183,18 +188,20 @@ def close(self):
183188
self.pid = p.pid
184189

185190
if self.timeout:
186-
condition = threading.Condition()
191+
time_condition = threading.Condition()
187192
self.logger.debug("Setting timeout to {}".format(self.timeout))
188193
timeout_thread = TimeoutThread(self.logger, self.timeout,
189-
condition, p)
194+
time_condition, p)
190195

191196
self.logger.debug("Waiting for process with PID {}".format(p.pid))
192197
p.wait()
198+
self.logger.debug("done waiting")
193199

194200
if self.timeout:
195201
e = timeout_thread.get_exception()
196202
if e:
197203
raise e
204+
198205
except KeyboardInterrupt as e:
199206
self.logger.info("Got KeyboardException while processing ",
200207
exc_info=True)
@@ -211,9 +218,16 @@ def close(self):
211218
self.logger.debug("{} -> {}".format(self.cmd, self.getretcode()))
212219
finally:
213220
if self.timeout != 0 and timeout_thread:
214-
with condition:
215-
condition.notifyAll()
221+
with time_condition:
222+
time_condition.notifyAll()
223+
224+
# The subprocess module does not close the write pipe descriptor
225+
# it fetched via OutputThread's fileno() so in order to gracefully
226+
# exit the read loop we have to close it here ourselves.
216227
output_thread.close()
228+
self.logger.debug("Waiting on output thread to finish reading")
229+
event.wait()
230+
217231
self.out = output_thread.getoutput()
218232
elapsed_time = time.time() - start_time
219233
self.logger.debug("Command {} took {} seconds".

0 commit comments

Comments
 (0)