Skip to content

gh-113148: Handle exceptions from threading.atexit functions #130594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we handle all exceptions there then test_atexit_after_shutdown fails. Maybe we should ignore specific exception from _register_atexit (that called from atexit handler)?

Copy link
Contributor Author

@sergey-miryanov sergey-miryanov Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OTOH _register_atexit is private, so we can change its behavior. I don't see that b61b818 relying on this particular exception.

atexit_call()
except:
th = current_thread()
th._invoke_excepthook(th)

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