mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	Issue #1068268: The subprocess module now handles EINTR in internal
os.waitpid and os.read system calls where appropriate.
This commit is contained in:
		
							parent
							
								
									fb501123e3
								
							
						
					
					
						commit
						cce211f88c
					
				
					 3 changed files with 38 additions and 4 deletions
				
			
		|  | @ -476,6 +476,16 @@ def _cleanup(): | ||||||
| STDOUT = -2 | STDOUT = -2 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def _eintr_retry_call(func, *args): | ||||||
|  |     while True: | ||||||
|  |         try: | ||||||
|  |             return func(*args) | ||||||
|  |         except OSError, e: | ||||||
|  |             if e.errno == errno.EINTR: | ||||||
|  |                 continue | ||||||
|  |             raise | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def call(*popenargs, **kwargs): | def call(*popenargs, **kwargs): | ||||||
|     """Run command with arguments.  Wait for command to complete, then |     """Run command with arguments.  Wait for command to complete, then | ||||||
|     return the returncode attribute. |     return the returncode attribute. | ||||||
|  | @ -1173,13 +1183,14 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, | ||||||
|                     os.close(errwrite) |                     os.close(errwrite) | ||||||
| 
 | 
 | ||||||
|                 # Wait for exec to fail or succeed; possibly raising exception |                 # Wait for exec to fail or succeed; possibly raising exception | ||||||
|                 data = os.read(errpipe_read, 1048576) # Exception limited to 1M |                 # Exception limited to 1M | ||||||
|  |                 data = _eintr_retry_call(os.read, errpipe_read, 1048576) | ||||||
|             finally: |             finally: | ||||||
|                 # be sure the FD is closed no matter what |                 # be sure the FD is closed no matter what | ||||||
|                 os.close(errpipe_read) |                 os.close(errpipe_read) | ||||||
| 
 | 
 | ||||||
|             if data != "": |             if data != "": | ||||||
|                 os.waitpid(self.pid, 0) |                 _eintr_retry_call(os.waitpid, self.pid, 0) | ||||||
|                 child_exception = pickle.loads(data) |                 child_exception = pickle.loads(data) | ||||||
|                 for fd in (p2cwrite, c2pread, errread): |                 for fd in (p2cwrite, c2pread, errread): | ||||||
|                     if fd is not None: |                     if fd is not None: | ||||||
|  | @ -1215,7 +1226,7 @@ def wait(self): | ||||||
|             """Wait for child process to terminate.  Returns returncode |             """Wait for child process to terminate.  Returns returncode | ||||||
|             attribute.""" |             attribute.""" | ||||||
|             if self.returncode is None: |             if self.returncode is None: | ||||||
|                 pid, sts = os.waitpid(self.pid, 0) |                 pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) | ||||||
|                 self._handle_exitstatus(sts) |                 self._handle_exitstatus(sts) | ||||||
|             return self.returncode |             return self.returncode | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| import sys | import sys | ||||||
| import signal | import signal | ||||||
| import os | import os | ||||||
|  | import errno | ||||||
| import tempfile | import tempfile | ||||||
| import time | import time | ||||||
| import re | import re | ||||||
|  | @ -772,11 +773,30 @@ def tearDown(self): | ||||||
|         ProcessTestCase.tearDown(self) |         ProcessTestCase.tearDown(self) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class HelperFunctionTests(unittest.TestCase): | ||||||
|  |     def test_eintr_retry_call(self): | ||||||
|  |         record_calls = [] | ||||||
|  |         def fake_os_func(*args): | ||||||
|  |             record_calls.append(args) | ||||||
|  |             if len(record_calls) == 2: | ||||||
|  |                 raise OSError(errno.EINTR, "fake interrupted system call") | ||||||
|  |             return tuple(reversed(args)) | ||||||
|  | 
 | ||||||
|  |         self.assertEqual((999, 256), | ||||||
|  |                          subprocess._eintr_retry_call(fake_os_func, 256, 999)) | ||||||
|  |         self.assertEqual([(256, 999)], record_calls) | ||||||
|  |         # This time there will be an EINTR so it will loop once. | ||||||
|  |         self.assertEqual((666,), | ||||||
|  |                          subprocess._eintr_retry_call(fake_os_func, 666)) | ||||||
|  |         self.assertEqual([(256, 999), (666,), (666,)], record_calls) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def test_main(): | def test_main(): | ||||||
|     unit_tests = (ProcessTestCase, |     unit_tests = (ProcessTestCase, | ||||||
|                   POSIXProcessTestCase, |                   POSIXProcessTestCase, | ||||||
|                   Win32ProcessTestCase, |                   Win32ProcessTestCase, | ||||||
|                   ProcessTestCaseNoPoll) |                   ProcessTestCaseNoPoll, | ||||||
|  |                   HelperFunctionTests) | ||||||
| 
 | 
 | ||||||
|     test_support.run_unittest(*unit_tests) |     test_support.run_unittest(*unit_tests) | ||||||
|     test_support.reap_children() |     test_support.reap_children() | ||||||
|  |  | ||||||
|  | @ -75,6 +75,9 @@ Library | ||||||
| - Issue #7481: When a threading.Thread failed to start it would leave the | - Issue #7481: When a threading.Thread failed to start it would leave the | ||||||
|   instance stuck in initial state and present in threading.enumerate(). |   instance stuck in initial state and present in threading.enumerate(). | ||||||
| 
 | 
 | ||||||
|  | - Issue #1068268: The subprocess module now handles EINTR in internal | ||||||
|  |   os.waitpid and os.read system calls where appropriate. | ||||||
|  | 
 | ||||||
| Extension Modules | Extension Modules | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gregory P. Smith
						Gregory P. Smith