@@ -908,6 +908,7 @@ class is implemented.
908908 self ._ident = None
909909 if _HAVE_THREAD_NATIVE_ID :
910910 self ._native_id = None
911+ self ._running_lock = None
911912 self ._tstate_lock = None
912913 self ._started = Event ()
913914 self ._is_stopped = False
@@ -926,13 +927,17 @@ def _reset_internal_locks(self, is_alive):
926927 # bpo-42350: If the fork happens when the thread is already stopped
927928 # (ex: after threading._shutdown() has been called), _tstate_lock
928929 # is None. Do nothing in this case.
930+ if self ._running_lock is not None :
931+ self ._running_lock ._at_fork_reinit ()
932+ self ._running_lock .acquire ()
929933 if self ._tstate_lock is not None :
930934 self ._tstate_lock ._at_fork_reinit ()
931935 self ._tstate_lock .acquire ()
932936 else :
933937 # The thread isn't alive after fork: it doesn't have a tstate
934938 # anymore.
935939 self ._is_stopped = True
940+ self ._running_lock = None
936941 self ._tstate_lock = None
937942
938943 def __repr__ (self ):
@@ -1019,6 +1024,14 @@ def _set_ident(self):
10191024 def _set_native_id (self ):
10201025 self ._native_id = get_native_id ()
10211026
1027+ def _set_running_lock (self ):
1028+ """
1029+ Set a lock object which will be released by the interpreter when
1030+ the target func has finished running.
1031+ """
1032+ self ._running_lock = _allocate_lock ()
1033+ self ._running_lock .acquire ()
1034+
10221035 def _set_tstate_lock (self ):
10231036 """
10241037 Set a lock object which will be released by the interpreter when
@@ -1035,6 +1048,7 @@ def _set_tstate_lock(self):
10351048 def _bootstrap_inner (self ):
10361049 try :
10371050 self ._set_ident ()
1051+ self ._set_running_lock ()
10381052 self ._set_tstate_lock ()
10391053 if _HAVE_THREAD_NATIVE_ID :
10401054 self ._set_native_id ()
@@ -1054,29 +1068,29 @@ def _bootstrap_inner(self):
10541068 self ._invoke_excepthook (self )
10551069 finally :
10561070 self ._delete ()
1071+ self ._running_lock .release ()
10571072
10581073 def _stop (self ):
10591074 # After calling ._stop(), .is_alive() returns False and .join() returns
1060- # immediately. ._tstate_lock must be released before calling ._stop().
1075+ # immediately. ._running_lock must be released before calling ._stop().
10611076 #
1062- # Normal case: C code at the end of the thread's life
1063- # (release_sentinel in _threadmodule.c) releases ._tstate_lock, and
1064- # that's detected by our ._wait_for_tstate_lock(), called by .join()
1077+ # Normal case: ._bootstrap_inner() releases ._running_lock, and
1078+ # that's detected by our ._wait_for_running_lock(), called by .join()
10651079 # and .is_alive(). Any number of threads _may_ call ._stop()
10661080 # simultaneously (for example, if multiple threads are blocked in
10671081 # .join() calls), and they're not serialized. That's harmless -
10681082 # they'll just make redundant rebindings of ._is_stopped and
1069- # ._tstate_lock . Obscure: we rebind ._tstate_lock last so that the
1070- # "assert self._is_stopped" in ._wait_for_tstate_lock () always works
1071- # (the assert is executed only if ._tstate_lock is None).
1083+ # ._running_lock . Obscure: we rebind ._running_lock last so that the
1084+ # "assert self._is_stopped" in ._wait_for_running_lock () always works
1085+ # (the assert is executed only if ._running_lock is None).
10721086 #
1073- # Special case: _main_thread releases ._tstate_lock via this
1087+ # Special case: _main_thread releases ._running_lock via this
10741088 # module's _shutdown() function.
1075- lock = self ._tstate_lock
1089+ lock = self ._running_lock
10761090 if lock is not None :
10771091 assert not lock .locked ()
10781092 self ._is_stopped = True
1079- self ._tstate_lock = None
1093+ self ._running_lock = None
10801094 if not self .daemon :
10811095 with _shutdown_locks_lock :
10821096 # Remove our lock and other released locks from _shutdown_locks
@@ -1123,20 +1137,17 @@ def join(self, timeout=None):
11231137 raise RuntimeError ("cannot join current thread" )
11241138
11251139 if timeout is None :
1126- self ._wait_for_tstate_lock ()
1140+ self ._wait_for_running_lock ()
11271141 else :
11281142 # the behavior of a negative timeout isn't documented, but
11291143 # historically .join(timeout=x) for x<0 has acted as if timeout=0
1130- self ._wait_for_tstate_lock (timeout = max (timeout , 0 ))
1131-
1132- def _wait_for_tstate_lock (self , block = True , timeout = - 1 ):
1133- # Issue #18808: wait for the thread state to be gone.
1134- # At the end of the thread's life, after all knowledge of the thread
1135- # is removed from C data structures, C code releases our _tstate_lock.
1136- # This method passes its arguments to _tstate_lock.acquire().
1137- # If the lock is acquired, the C code is done, and self._stop() is
1138- # called. That sets ._is_stopped to True, and ._tstate_lock to None.
1139- lock = self ._tstate_lock
1144+ self ._wait_for_running_lock (timeout = max (timeout , 0 ))
1145+
1146+ def _wait_for_running_lock (self , block = True , timeout = - 1 ):
1147+ # This method passes its arguments to _running_lock.acquire().
1148+ # If the lock is acquired, the python code is done, and self._stop() is
1149+ # called. That sets ._is_stopped to True, and ._running_lock to None.
1150+ lock = self ._running_lock
11401151 if lock is None :
11411152 # already determined that the C code is done
11421153 assert self ._is_stopped
@@ -1207,7 +1218,7 @@ def is_alive(self):
12071218 assert self ._initialized , "Thread.__init__() not called"
12081219 if self ._is_stopped or not self ._started .is_set ():
12091220 return False
1210- self ._wait_for_tstate_lock (False )
1221+ self ._wait_for_running_lock (False )
12111222 return not self ._is_stopped
12121223
12131224 @property
@@ -1417,7 +1428,7 @@ class _MainThread(Thread):
14171428
14181429 def __init__ (self ):
14191430 Thread .__init__ (self , name = "MainThread" , daemon = False )
1420- self ._set_tstate_lock ()
1431+ self ._set_running_lock ()
14211432 self ._started .set ()
14221433 self ._set_ident ()
14231434 if _HAVE_THREAD_NATIVE_ID :
@@ -1558,7 +1569,7 @@ def _shutdown():
15581569 # dubious, but some code does it. We can't wait for C code to release
15591570 # the main thread's tstate_lock - that won't happen until the interpreter
15601571 # is nearly dead. So we release it here. Note that just calling _stop()
1561- # isn't enough: other threads may already be waiting on _tstate_lock .
1572+ # isn't enough: other threads may already be waiting on _running_lock .
15621573 if _main_thread ._is_stopped :
15631574 # _shutdown() was already called
15641575 return
@@ -1573,12 +1584,13 @@ def _shutdown():
15731584
15741585 # Main thread
15751586 if _main_thread .ident == get_ident ():
1576- tlock = _main_thread ._tstate_lock
1577- # The main thread isn't finished yet, so its thread state lock can't
1587+ assert _main_thread ._tstate_lock is None
1588+ running_lock = _main_thread ._running_lock
1589+ # The main thread isn't finished yet, so its running lock can't
15781590 # have been released.
1579- assert tlock is not None
1580- assert tlock .locked ()
1581- tlock .release ()
1591+ assert running_lock is not None
1592+ assert running_lock .locked ()
1593+ running_lock .release ()
15821594 _main_thread ._stop ()
15831595 else :
15841596 # bpo-1596321: _shutdown() must be called in the main thread.
0 commit comments