ActiveState Code

Recipe 496960: thread2 -- killable threads


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
 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)

Discussion

There are some issues with this code, refer to http://sebulba.wikispaces.com/recipe+thread2 for more info and details. Requires ctypes.

Comments

  1. 1. At 10:15 a.m. on 18 aug 2006, Thomas Heller said:

    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.

  2. 2. At noon on 18 aug 2006, tomer filiba (the author) said:

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

  3. 3. At 8:57 a.m. on 28 may 2008, nicholas tung said:

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

  4. 4. At 1:05 p.m. on 7 jun 2008, Adam Olsen said:

    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.

Sign in to comment