-
-
Save erantapaa/1e0872301cc69efc2b343516a3fa5136 to your computer and use it in GitHub Desktop.
Nasty hack to raise exception for other threads
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# liuw | |
# Nasty hack to raise exception for other threads | |
import ctypes # Calm down, this has become standard library since 2.5 | |
import threading | |
import time | |
NULL = 0 | |
def ctype_async_raise(thread_obj, exception): | |
found = False | |
target_tid = 0 | |
for tid, tobj in threading._active.items(): | |
if tobj is thread_obj: | |
found = True | |
target_tid = tid | |
break | |
if not found: | |
raise ValueError("Invalid thread object") | |
ret = ctypes.pythonapi.PyThreadState_SetAsyncExc(target_tid, ctypes.py_object(exception)) | |
# ref: http://docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc | |
if ret == 0: | |
raise ValueError("Invalid thread ID") | |
elif ret > 1: | |
# Huh? Why would we notify more than one threads? | |
# Because we punch a hole into C level interpreter. | |
# So it is better to clean up the mess. | |
ctypes.pythonapi.PyThreadState_SetAsyncExc(target_tid, NULL) | |
raise SystemError("PyThreadState_SetAsyncExc failed") | |
print "Successfully set asynchronized exception for", target_tid | |
def f(): | |
try: | |
while True: | |
time.sleep(1) | |
finally: | |
print "Exited" | |
t = threading.Thread(target=f) | |
t.start() | |
print "Thread started" | |
print t.isAlive() | |
time.sleep(5) | |
ctype_async_raise(t, SystemExit) | |
t.join() | |
print t.isAlive() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(a comment from the original gist)
Hi,
According to http://docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc the target_tid parameter is expected to be a C long int. As a consequence the formula in line 23 does not work as expected on some CPython engines including mine (CPython 2.6, 2.7 and 3.3 on MacOSX Mavericks) : ret is always 0.
So I changed your line 23 with this one and it works for me as it should work on any CPython engine: