Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Lib/test/test_interpreters/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,43 @@ def task():

self.assertEqual(os.read(r_interp, 1), FINISHED)

def test_subthreads_raise_exception_from_atexit(self):
r_interp, w_interp = self.pipe()

FINISHED = b'F'

prev_excepthook = threading.excepthook
interp = interpreters.create()
interp.exec(f"""if True:
from io import StringIO
import os
import threading
import time

threading.excepthook = lambda args: args

done = False
def notify_fini():
global done
done = True

def raise_exception():
raise Exception('AtexitException')
threading._register_atexit(notify_fini)
threading._register_atexit(raise_exception)

def task():
while not done:
time.sleep(0.1)
os.write({w_interp}, {FINISHED!r})
t = threading.Thread(target=task)
t.start()
""")
interp.close()

self.assertEqual(threading.excepthook, prev_excepthook)
self.assertEqual(os.read(r_interp, 1), FINISHED)

def test_created_with_capi(self):
script = dedent(f"""
import {interpreters.__name__} as interpreters
Expand Down
6 changes: 5 additions & 1 deletion Lib/threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -1546,7 +1546,11 @@ def _shutdown():
# Call registered threading atexit functions before threads are joined.
# Order is reversed, similar to atexit.
for atexit_call in reversed(_threading_atexits):
atexit_call()
try:
atexit_call()
except:
th = current_thread()
th._invoke_excepthook(th)

if _is_main_interpreter():
_main_thread._handle._set_done()
Expand Down
Loading