mirror of
https://github.com/python/cpython.git
synced 2025-10-06 23:21:06 +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 @@ import subprocess
|
||||||
|
|
||||||
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 @@ class ThreadTests(BaseTestCase):
|
||||||
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 @@ class _MainThread(Thread):
|
||||||
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 @@ from _thread import stack_size
|
||||||
# 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