mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Issue #18882: Add threading.main_thread() function.
This commit is contained in:
		
							parent
							
								
									3c56145638
								
							
						
					
					
						commit
						58b5c5ad14
					
				
					 4 changed files with 102 additions and 23 deletions
				
			
		|  | @ -57,6 +57,15 @@ This module defines the following functions: | ||||||
|    and threads that have not yet been started. |    and threads that have not yet been started. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | .. function:: main_thread() | ||||||
|  | 
 | ||||||
|  |    Return the main :class:`Thread` object.  In normal conditions, the | ||||||
|  |    main thread is the thread from which the Python interpreter was | ||||||
|  |    started. | ||||||
|  | 
 | ||||||
|  |    .. versionadded:: 3.4 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| .. function:: settrace(func) | .. function:: settrace(func) | ||||||
| 
 | 
 | ||||||
|    .. index:: single: trace function |    .. index:: single: trace function | ||||||
|  |  | ||||||
|  | @ -21,6 +21,15 @@ | ||||||
| 
 | 
 | ||||||
| from test import lock_tests | from test import lock_tests | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | # Between fork() and exec(), only async-safe functions are allowed (issues | ||||||
|  | # #12316 and #11870), and fork() from a worker thread is known to trigger | ||||||
|  | # problems with some operating systems (issue #3863): skip problematic tests | ||||||
|  | # on platforms known to behave badly. | ||||||
|  | platforms_to_skip = ('freebsd4', 'freebsd5', 'freebsd6', 'netbsd5', | ||||||
|  |                      'hp-ux11') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # A trivial mutable counter. | # A trivial mutable counter. | ||||||
| class Counter(object): | class Counter(object): | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|  | @ -468,16 +477,71 @@ def test_is_alive_after_fork(self): | ||||||
|                 pid, status = os.waitpid(pid, 0) |                 pid, status = os.waitpid(pid, 0) | ||||||
|                 self.assertEqual(0, status) |                 self.assertEqual(0, status) | ||||||
| 
 | 
 | ||||||
|  |     def test_main_thread(self): | ||||||
|  |         main = threading.main_thread() | ||||||
|  |         self.assertEqual(main.name, 'MainThread') | ||||||
|  |         self.assertEqual(main.ident, threading.current_thread().ident) | ||||||
|  |         self.assertEqual(main.ident, threading.get_ident()) | ||||||
|  | 
 | ||||||
|  |         def f(): | ||||||
|  |             self.assertNotEqual(threading.main_thread().ident, | ||||||
|  |                                 threading.current_thread().ident) | ||||||
|  |         th = threading.Thread(target=f) | ||||||
|  |         th.start() | ||||||
|  |         th.join() | ||||||
|  | 
 | ||||||
|  |     @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") | ||||||
|  |     @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") | ||||||
|  |     def test_main_thread_after_fork(self): | ||||||
|  |         code = """if 1: | ||||||
|  |             import os, threading | ||||||
|  | 
 | ||||||
|  |             pid = os.fork() | ||||||
|  |             if pid == 0: | ||||||
|  |                 main = threading.main_thread() | ||||||
|  |                 print(main.name) | ||||||
|  |                 print(main.ident == threading.current_thread().ident) | ||||||
|  |                 print(main.ident == threading.get_ident()) | ||||||
|  |             else: | ||||||
|  |                 os.waitpid(pid, 0) | ||||||
|  |         """ | ||||||
|  |         _, out, err = assert_python_ok("-c", code) | ||||||
|  |         data = out.decode().replace('\r', '') | ||||||
|  |         self.assertEqual(err, b"") | ||||||
|  |         self.assertEqual(data, "MainThread\nTrue\nTrue\n") | ||||||
|  | 
 | ||||||
|  |     @unittest.skipIf(sys.platform in platforms_to_skip, "due to known OS bug") | ||||||
|  |     @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") | ||||||
|  |     @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") | ||||||
|  |     def test_main_thread_after_fork_from_nonmain_thread(self): | ||||||
|  |         code = """if 1: | ||||||
|  |             import os, threading, sys | ||||||
|  | 
 | ||||||
|  |             def f(): | ||||||
|  |                 pid = os.fork() | ||||||
|  |                 if pid == 0: | ||||||
|  |                     main = threading.main_thread() | ||||||
|  |                     print(main.name) | ||||||
|  |                     print(main.ident == threading.current_thread().ident) | ||||||
|  |                     print(main.ident == threading.get_ident()) | ||||||
|  |                     # stdout is fully buffered because not a tty, | ||||||
|  |                     # we have to flush before exit. | ||||||
|  |                     sys.stdout.flush() | ||||||
|  |                 else: | ||||||
|  |                     os.waitpid(pid, 0) | ||||||
|  | 
 | ||||||
|  |             th = threading.Thread(target=f) | ||||||
|  |             th.start() | ||||||
|  |             th.join() | ||||||
|  |         """ | ||||||
|  |         _, out, err = assert_python_ok("-c", code) | ||||||
|  |         data = out.decode().replace('\r', '') | ||||||
|  |         self.assertEqual(err, b"") | ||||||
|  |         self.assertEqual(data, "Thread-1\nTrue\nTrue\n") | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class ThreadJoinOnShutdown(BaseTestCase): | class ThreadJoinOnShutdown(BaseTestCase): | ||||||
| 
 | 
 | ||||||
|     # Between fork() and exec(), only async-safe functions are allowed (issues |  | ||||||
|     # #12316 and #11870), and fork() from a worker thread is known to trigger |  | ||||||
|     # problems with some operating systems (issue #3863): skip problematic tests |  | ||||||
|     # on platforms known to behave badly. |  | ||||||
|     platforms_to_skip = ('freebsd4', 'freebsd5', 'freebsd6', 'netbsd5', |  | ||||||
|                          'hp-ux11') |  | ||||||
| 
 |  | ||||||
|     def _run_and_join(self, script): |     def _run_and_join(self, script): | ||||||
|         script = """if 1: |         script = """if 1: | ||||||
|             import sys, os, time, threading |             import sys, os, time, threading | ||||||
|  |  | ||||||
|  | @ -840,20 +840,6 @@ def __init__(self): | ||||||
|         with _active_limbo_lock: |         with _active_limbo_lock: | ||||||
|             _active[self._ident] = self |             _active[self._ident] = self | ||||||
| 
 | 
 | ||||||
|     def _exitfunc(self): |  | ||||||
|         self._stop() |  | ||||||
|         t = _pickSomeNonDaemonThread() |  | ||||||
|         while t: |  | ||||||
|             t.join() |  | ||||||
|             t = _pickSomeNonDaemonThread() |  | ||||||
|         self._delete() |  | ||||||
| 
 |  | ||||||
| def _pickSomeNonDaemonThread(): |  | ||||||
|     for t in enumerate(): |  | ||||||
|         if not t.daemon and t.is_alive(): |  | ||||||
|             return t |  | ||||||
|     return None |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| # Dummy thread class to represent threads not started here. | # Dummy thread class to represent threads not started here. | ||||||
| # These aren't garbage collected when they die, nor can they be waited for. | # These aren't garbage collected when they die, nor can they be waited for. | ||||||
|  | @ -915,7 +901,24 @@ def enumerate(): | ||||||
| # and make it available for the interpreter | # and make it available for the interpreter | ||||||
| # (Py_Main) as threading._shutdown. | # (Py_Main) as threading._shutdown. | ||||||
| 
 | 
 | ||||||
| _shutdown = _MainThread()._exitfunc | _main_thread = _MainThread() | ||||||
|  | 
 | ||||||
|  | def _shutdown(): | ||||||
|  |     _main_thread._stop() | ||||||
|  |     t = _pickSomeNonDaemonThread() | ||||||
|  |     while t: | ||||||
|  |         t.join() | ||||||
|  |         t = _pickSomeNonDaemonThread() | ||||||
|  |     _main_thread._delete() | ||||||
|  | 
 | ||||||
|  | def _pickSomeNonDaemonThread(): | ||||||
|  |     for t in enumerate(): | ||||||
|  |         if not t.daemon and t.is_alive(): | ||||||
|  |             return t | ||||||
|  |     return None | ||||||
|  | 
 | ||||||
|  | def main_thread(): | ||||||
|  |     return _main_thread | ||||||
| 
 | 
 | ||||||
| # get thread-local implementation, either from the thread | # get thread-local implementation, either from the thread | ||||||
| # module, or from the python fallback | # module, or from the python fallback | ||||||
|  | @ -933,12 +936,13 @@ def _after_fork(): | ||||||
| 
 | 
 | ||||||
|     # Reset _active_limbo_lock, in case we forked while the lock was held |     # Reset _active_limbo_lock, in case we forked while the lock was held | ||||||
|     # by another (non-forked) thread.  http://bugs.python.org/issue874900 |     # by another (non-forked) thread.  http://bugs.python.org/issue874900 | ||||||
|     global _active_limbo_lock |     global _active_limbo_lock, _main_thread | ||||||
|     _active_limbo_lock = _allocate_lock() |     _active_limbo_lock = _allocate_lock() | ||||||
| 
 | 
 | ||||||
|     # fork() only copied the current thread; clear references to others. |     # fork() only copied the current thread; clear references to others. | ||||||
|     new_active = {} |     new_active = {} | ||||||
|     current = current_thread() |     current = current_thread() | ||||||
|  |     _main_thread = current | ||||||
|     with _active_limbo_lock: |     with _active_limbo_lock: | ||||||
|         for thread in _enumerate(): |         for thread in _enumerate(): | ||||||
|             # Any lock/condition variable may be currently locked or in an |             # Any lock/condition variable may be currently locked or in an | ||||||
|  |  | ||||||
|  | @ -54,6 +54,8 @@ Core and Builtins | ||||||
| Library | Library | ||||||
| ------- | ------- | ||||||
| 
 | 
 | ||||||
|  | - Issue #18882: Add threading.main_thread() function. | ||||||
|  | 
 | ||||||
| - Issue #18901: The sunau getparams method now returns a namedtuple rather than | - Issue #18901: The sunau getparams method now returns a namedtuple rather than | ||||||
|   a plain tuple.  Patch by Claudiu Popa. |   a plain tuple.  Patch by Claudiu Popa. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andrew Svetlov
						Andrew Svetlov