mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	[3.13] gh-109746: Make _thread.start_new_thread delete state of new thread on its startup failure (GH-109761) (GH-127171)
If Python fails to start newly created thread
due to failure of underlying PyThread_start_new_thread() call,
its state should be removed from interpreter' thread states list
to avoid its double cleanup.
(cherry picked from commit ca3ea9ad05)
Co-authored-by: Radislav Chugunov <52372310+chgnrdv@users.noreply.github.com>
			
			
This commit is contained in:
		
							parent
							
								
									950daf8765
								
							
						
					
					
						commit
						75ef92da29
					
				
					 4 changed files with 40 additions and 1 deletions
				
			
		|  | @ -1171,6 +1171,41 @@ def __del__(self): | ||||||
|         self.assertEqual(out.strip(), b"OK") |         self.assertEqual(out.strip(), b"OK") | ||||||
|         self.assertIn(b"can't create new thread at interpreter shutdown", err) |         self.assertIn(b"can't create new thread at interpreter shutdown", err) | ||||||
| 
 | 
 | ||||||
|  |     def test_start_new_thread_failed(self): | ||||||
|  |         # gh-109746: if Python fails to start newly created thread | ||||||
|  |         # due to failure of underlying PyThread_start_new_thread() call, | ||||||
|  |         # its state should be removed from interpreter' thread states list | ||||||
|  |         # to avoid its double cleanup | ||||||
|  |         try: | ||||||
|  |             from resource import setrlimit, RLIMIT_NPROC | ||||||
|  |         except ImportError as err: | ||||||
|  |             self.skipTest(err)  # RLIMIT_NPROC is specific to Linux and BSD | ||||||
|  |         code = """if 1: | ||||||
|  |             import resource | ||||||
|  |             import _thread | ||||||
|  | 
 | ||||||
|  |             def f(): | ||||||
|  |                 print("shouldn't be printed") | ||||||
|  | 
 | ||||||
|  |             limits = resource.getrlimit(resource.RLIMIT_NPROC) | ||||||
|  |             [_, hard] = limits | ||||||
|  |             resource.setrlimit(resource.RLIMIT_NPROC, (0, hard)) | ||||||
|  | 
 | ||||||
|  |             try: | ||||||
|  |                 _thread.start_new_thread(f, ()) | ||||||
|  |             except RuntimeError: | ||||||
|  |                 print('ok') | ||||||
|  |             else: | ||||||
|  |                 print('skip') | ||||||
|  |         """ | ||||||
|  |         _, out, err = assert_python_ok("-u", "-c", code) | ||||||
|  |         out = out.strip() | ||||||
|  |         if out == b'skip': | ||||||
|  |             self.skipTest('RLIMIT_NPROC had no effect; probably superuser') | ||||||
|  |         self.assertEqual(out, b'ok') | ||||||
|  |         self.assertEqual(err, b'') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class ThreadJoinOnShutdown(BaseTestCase): | class ThreadJoinOnShutdown(BaseTestCase): | ||||||
| 
 | 
 | ||||||
|     def _run_and_join(self, script): |     def _run_and_join(self, script): | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | If :func:`!_thread.start_new_thread` fails to start a new thread, it deletes its state from interpreter and thus avoids its repeated cleanup on finalization. | ||||||
|  | @ -421,6 +421,7 @@ ThreadHandle_start(ThreadHandle *self, PyObject *func, PyObject *args, | ||||||
|     PyThread_handle_t os_handle; |     PyThread_handle_t os_handle; | ||||||
|     if (PyThread_start_joinable_thread(thread_run, boot, &ident, &os_handle)) { |     if (PyThread_start_joinable_thread(thread_run, boot, &ident, &os_handle)) { | ||||||
|         PyThreadState_Clear(boot->tstate); |         PyThreadState_Clear(boot->tstate); | ||||||
|  |         PyThreadState_Delete(boot->tstate); | ||||||
|         thread_bootstate_free(boot, 1); |         thread_bootstate_free(boot, 1); | ||||||
|         PyErr_SetString(ThreadError, "can't start new thread"); |         PyErr_SetString(ThreadError, "can't start new thread"); | ||||||
|         goto start_failed; |         goto start_failed; | ||||||
|  |  | ||||||
|  | @ -1811,7 +1811,9 @@ tstate_delete_common(PyThreadState *tstate, int release_gil) | ||||||
|     if (tstate->_status.bound_gilstate) { |     if (tstate->_status.bound_gilstate) { | ||||||
|         unbind_gilstate_tstate(tstate); |         unbind_gilstate_tstate(tstate); | ||||||
|     } |     } | ||||||
|     unbind_tstate(tstate); |     if (tstate->_status.bound) { | ||||||
|  |         unbind_tstate(tstate); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // XXX Move to PyThreadState_Clear()?
 |     // XXX Move to PyThreadState_Clear()?
 | ||||||
|     clear_datastack(tstate); |     clear_datastack(tstate); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka