"""Demonstrate POSIX thread safety problems with the subprocess module.""" from __future__ import print_function import subprocess import sys import threading import time class Result(object): def __init__(self, name, value, expected): self.name = name self.value = value self.expected = expected def Print(self): print(' {}\t= {}, expected {} {}'.format( self.name, self.value, self.expected, '*** MISMATCH ***' if self.value != self.expected else '')) def TestPopen(popen): """Test a particular 'Popen' method, and print results. Args: popen: (callable) the Popen method to test (ex subproccess.Popen). """ start_time = time.time() results = [] subproc = popen(['/bin/sleep', '60']) results.append(Result('r0', subproc.returncode, None)) def Interrupt(): """Kill the subprocess, and capture returncode.""" # should be None because process hasn't exited yet results.append(Result('ri0', subproc.returncode, None)) # kill the subproc, and wait for the result subproc.kill() subproc.wait() # should be -9 because subproc was killed results.append(Result('ri1', subproc.returncode, -9)) # another wait() should be a no-op, because subproc has already finished subproc.wait() # should still be -9 because subproc was killed results.append(Result('ri2', subproc.returncode, -9)) # start a separate thread to run Interrupt after a 1 second delay t = threading.Timer(1, Interrupt) t.start() results.append(Result('r1', subproc.returncode, None)) # wait for subproc to finish; it will be killed before # successfully finishing, when Interrupt fires subproc.wait() # should be -9 because subproc was killed results.append(Result('r2', subproc.returncode, -9)) # another wait() should be a no-op because subproc has already finished subproc.wait() # should still be -9 because subproc was killed results.append(Result('r3', subproc.returncode, -9)) t.join() print('Results with %r:' % popen) for r in results: r.Print() print('elapsed = %f seconds' % (time.time() - start_time)) def main(): print('sys.version = %s' % sys.version) print('--------') TestPopen(subprocess.Popen) if __name__ == '__main__': main()