- Fix Issue #1703448: A joined thread could show up in the
  threading.enumerate() list after the join() for a brief period until
  it actually exited.
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 9e26536..4f49d7f 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -236,6 +236,24 @@
             """])
         self.assertEqual(rc, 42)
 
+    def test_enumerate_after_join(self):
+        # Try hard to trigger #1703448: a thread is still returned in
+        # threading.enumerate() after it has been join()ed.
+        enum = threading.enumerate
+        old_interval = sys.getcheckinterval()
+        sys.setcheckinterval(1)
+        try:
+            for i in xrange(1, 1000):
+                t = threading.Thread(target=lambda: None)
+                t.start()
+                t.join()
+                l = enum()
+                self.assertFalse(t in l,
+                    "#1703448 triggered after %d trials: %s" % (i, l))
+        finally:
+            sys.setcheckinterval(old_interval)
+
+
 class ThreadingExceptionTests(unittest.TestCase):
     # A RuntimeError should be raised if Thread.start() is called
     # multiple times.
diff --git a/Lib/threading.py b/Lib/threading.py
index 98d15b2..50cbb06 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -515,11 +515,14 @@
                 if __debug__:
                     self._note("%s.__bootstrap(): normal return", self)
         finally:
-            self.__stop()
-            try:
-                self.__delete()
-            except:
-                pass
+            with _active_limbo_lock:
+                self.__stop()
+                try:
+                    # We don't call self.__delete() because it also
+                    # grabs _active_limbo_lock.
+                    del _active[_get_ident()]
+                except:
+                    pass
 
     def __stop(self):
         with self.__block: