diff -r 330c7aa2922b Lib/test/test_threading.py --- a/Lib/test/test_threading.py Tue Jul 09 19:15:43 2013 +0200 +++ b/Lib/test/test_threading.py Mon Jul 15 20:04:36 2013 -0400 @@ -444,6 +444,30 @@ self.assertEqual(out, b'') self.assertEqual(err, b'') + @unittest.skipUnless(hasattr(os, 'fork'), "needs os.fork()") + def test_is_alive_after_fork(self): + # Try hard to trigger #18418: is_alive() could sometimes be True on + # threads that vanished after a fork. + + old_interval = sys.getswitchinterval() + try: + # Make the bug more likely to manifest. + sys.setswitchinterval(1e-6) + + for i in range(1, 20): + t = threading.Thread(target=lambda: None) + t.start() + pid = os.fork() + if pid == 0: + os._exit(1 if t.is_alive() else 0) + else: + (_, exit_status) = os.waitpid(pid, 0) + self.assertEqual( + 0, exit_status, + "#18418 triggered after %d trials" % i) + finally: + sys.setswitchinterval(old_interval) + class ThreadJoinOnShutdown(BaseTestCase): diff -r 330c7aa2922b Lib/threading.py --- a/Lib/threading.py Tue Jul 09 19:15:43 2013 +0200 +++ b/Lib/threading.py Mon Jul 15 20:04:36 2013 -0400 @@ -623,10 +623,13 @@ def _bootstrap_inner(self): try: self._set_ident() - self._started.set() + # Issue #18418: Order matters, since another thread can fork at any + # time. Add self to _active, so we'll be marked dead after a fork, + # then set __started. Thus is_alive() must be False after a fork. with _active_limbo_lock: _active[self._ident] = self del _limbo[self] + self._started.set() if _trace_hook: _sys.settrace(_trace_hook)