[Python-checkins] cpython: Issue #18882: Add threading.main_thread() function.

andrew.svetlov python-checkins at python.org
Wed Sep 4 06:01:26 CEST 2013


http://hg.python.org/cpython/rev/96e55a1a0de7
changeset:   85520:96e55a1a0de7
user:        Andrew Svetlov <andrew.svetlov at gmail.com>
date:        Wed Sep 04 07:01:07 2013 +0300
summary:
  Issue #18882: Add threading.main_thread() function.

files:
  Doc/library/threading.rst  |   9 +++
  Lib/test/test_threading.py |  78 +++++++++++++++++++++++--
  Lib/threading.py           |  36 ++++++-----
  Misc/NEWS                  |   2 +
  4 files changed, 102 insertions(+), 23 deletions(-)


diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -57,6 +57,15 @@
    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)
 
    .. index:: single: trace function
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -21,6 +21,15 @@
 
 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.
 class Counter(object):
     def __init__(self):
@@ -468,16 +477,71 @@
                 pid, status = os.waitpid(pid, 0)
                 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):
 
-    # 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):
         script = """if 1:
             import sys, os, time, threading
diff --git a/Lib/threading.py b/Lib/threading.py
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -840,20 +840,6 @@
         with _active_limbo_lock:
             _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.
 # These aren't garbage collected when they die, nor can they be waited for.
@@ -915,7 +901,24 @@
 # and make it available for the interpreter
 # (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
 # module, or from the python fallback
@@ -933,12 +936,13 @@
 
     # Reset _active_limbo_lock, in case we forked while the lock was held
     # 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()
 
     # fork() only copied the current thread; clear references to others.
     new_active = {}
     current = current_thread()
+    _main_thread = current
     with _active_limbo_lock:
         for thread in _enumerate():
             # Any lock/condition variable may be currently locked or in an
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -54,6 +54,8 @@
 Library
 -------
 
+- Issue #18882: Add threading.main_thread() function.
+
 - Issue #18901: The sunau getparams method now returns a namedtuple rather than
   a plain tuple.  Patch by Claudiu Popa.
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list