@@ -1247,6 +1247,61 @@ def __del__(self):
12471247        self .assertEqual (err , b"" )
12481248        self .assertIn (b"all clear" , out )
12491249
1250+     @support .subTests ('lock_class_name' , ['Lock' , 'RLock' ]) 
1251+     def  test_acquire_daemon_thread_lock_in_finalization (self , lock_class_name ):
1252+         # gh-123940: Py_Finalize() prevents other threads from running Python 
1253+         # code (and so, releasing locks), so acquiring a locked lock can not 
1254+         # succeed. 
1255+         # We raise an exception rather than hang. 
1256+         code  =  textwrap .dedent (f""" 
1257+             import threading 
1258+             import time 
1259+ 
1260+             thread_started_event = threading.Event() 
1261+ 
1262+             lock = threading.{ lock_class_name }  
1263+             def loop(): 
1264+                 if { lock_class_name !r}  
1265+                     lock.acquire() 
1266+                 with lock: 
1267+                     thread_started_event.set() 
1268+                     while True: 
1269+                         time.sleep(1) 
1270+ 
1271+             uncontested_lock = threading.{ lock_class_name }  
1272+ 
1273+             class Cycle: 
1274+                 def __init__(self): 
1275+                     self.self_ref = self 
1276+                     self.thr = threading.Thread( 
1277+                         target=loop, daemon=True) 
1278+                     self.thr.start() 
1279+                     thread_started_event.wait() 
1280+ 
1281+                 def __del__(self): 
1282+                     assert self.thr.is_alive() 
1283+ 
1284+                     # We *can* acquire an unlocked lock 
1285+                     uncontested_lock.acquire() 
1286+                     if { lock_class_name !r}  
1287+                         uncontested_lock.acquire() 
1288+ 
1289+                     # Acquiring a locked one fails 
1290+                     try: 
1291+                         lock.acquire() 
1292+                     except PythonFinalizationError: 
1293+                         assert self.thr.is_alive() 
1294+                         print('got the correct exception!') 
1295+ 
1296+             # Cycle holds a reference to itself, which ensures it is 
1297+             # cleaned up during the GC that runs after daemon threads 
1298+             # have been forced to exit during finalization. 
1299+             Cycle() 
1300+         """ )
1301+         rc , out , err  =  assert_python_ok ("-c" , code )
1302+         self .assertEqual (err , b"" )
1303+         self .assertIn (b"got the correct exception" , out )
1304+ 
12501305    def  test_start_new_thread_failed (self ):
12511306        # gh-109746: if Python fails to start newly created thread 
12521307        # due to failure of underlying PyThread_start_new_thread() call, 
0 commit comments