Skip to content
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

With asynchronous exception raised, thread.is_alive() unexpectedly blocks in Python 3.11 with coverage #1626

Open
andersk opened this issue May 16, 2023 · 2 comments
Labels
bug Something isn't working

Comments

@andersk
Copy link
Contributor

andersk commented May 16, 2023

Describe the bug

This program (minimized from Zulip’s TimeoutTestCase.test_timeout_warn) succeeds in Python 3.10 without coverage, 3.10 with coverage, and 3.11 without coverage, but it fails in 3.11 with coverage.

import ctypes
import threading
import time

thread = threading.Thread(target=lambda: time.sleep(1))
thread.daemon = True
thread.start()
time.sleep(0.1)
ctypes.pythonapi.PyThreadState_SetAsyncExc(
    ctypes.c_ulong(thread.ident), ctypes.py_object(TimeoutError)
)
time.sleep(0.1)
thread.is_alive()
time.sleep(0.1)
assert thread.is_alive()

The program spawns sleep(1) in a thread, then uses the Python C API to raise an asynchronous exception within it, which has no immediate effect since it’s still sleeping. Both thread.is_alive() calls should immediately return True. But in Python 3.11 with coverage, it seems the first thread.is_alive() call unexpectedly blocks until the thread’s sleep(1) finishes before returning, so the second thread.is_alive() call returns False.

A git bisect of Python shows that this started failing with

To Reproduce

Run python3.11 -m coverage run test.py. I’m using the latest coverage==7.2.5 on Linux x86-64 (NixOS 23.05).

@andersk andersk added bug Something isn't working needs triage labels May 16, 2023
@nedbat
Copy link
Owner

nedbat commented May 17, 2023

I have no idea what that change in CPython did, or how coverage affects it. If you have any more details about why this might be happening, I'd appreciate them.

I also don't understand why ctypes is needed to demonstrate the problem.

@andersk
Copy link
Contributor Author

andersk commented May 17, 2023

I’m not sure whether ctypes is necessary to demonstrate this problem, but I can say that ctypes is necessary to call PyThreadState_SetAsyncExc (“To prevent naive misuse, you must write your own C extension to call this.”), which is necessary to implement a timeout mechanism for an arbitrary function (see the func_timeout package, for example).

I don’t have more information about where the problem might be yet, but I’ll let you know if I find something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants