Welcome, guest | Sign In | My Account | Store | Cart

A little module that extends the threading's module functionality -- allows one thread to raise exceptions in the context of another thread. By raising SystemExit, you can finally kill python threads :)

Python, 30 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import threading
import ctypes
 
 
def _async_raise(tid, excobj):
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
    if res == 0:
        raise ValueError("nonexistent thread id")
    elif res > 1:
        # """if it returns a number greater than one, you're in trouble, 
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")
 
class Thread(threading.Thread):
    def raise_exc(self, excobj):
        assert self.isAlive(), "thread must be started"
        for tid, tobj in threading._active.items():
            if tobj is self:
                _async_raise(tid, excobj)
                return
        
        # the thread was alive when we entered the loop, but was not found 
        # in the dict, hence it must have been already terminated. should we raise
        # an exception here? silently ignore?
    
    def terminate(self):
        # must raise the SystemExit type, instead of a SystemExit() instance
        # due to a bug in PyThreadState_SetAsyncExc
        self.raise_exc(SystemExit)

There are some issues with this code, refer to http://tomerfiliba.com/recipes/Thread2/ for more info and details. Requires ctypes.

6 comments

Thomas Heller 15 years, 3 months ago  # | flag

Small bug in the recipe. The second call to PyThreadState_SetAsyncEx must use 'None' as second parameter, otherwise it will not work correctly on 64-bit plaforms:

ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)

'None' will be passed as pointer parameter, '0' would be passed as integer.

tomer filiba (author) 15 years, 3 months ago  # | flag

this code is not up-to-date. see the link i gave for the full (updated) recipe

nicholas tung 13 years, 6 months ago  # | flag

it's easier in python3. in python3 you can just call _stop() on the thread object.

Adam Olsen 13 years, 6 months ago  # | flag

No, ._stop() is unrelated. A thread calls it when it finishes, to mark itself as stopped and wake up anybody who called .wait(). The naming is poor - ._mark_as_stopped() would have been a better choice.

Abdelrahman Hussein 11 years, 9 months ago  # | flag

well, for me(using python2.6) on ubuntu 9.10 i always got invalid thread id when testing the script any idea why ??

Andrew Abrahamowicz 11 years, 8 months ago  # | flag

Abdelrahman:

If you change lines 6 and 12 to :

*res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(exctype))*

and

*ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), 0)*

which wrap tid as ctypes.c_long, you'll find it works!