[Python-Dev] PyThreadState_SetAsyncExc bug?

tomer filiba tomerfiliba at gmail.com
Fri Aug 11 11:24:26 CEST 2006


while working on a library for raising exceptions in the context
of another thread, i've come across a bug in PyThreadState_SetAsyncExc.
if i raise an instance, sys.exc_info() confuses the exception value for
the exception type, and the exception value is set None. if i raise the
type itself, the interpreter creates an instance internally, but then i can't
pass arguments to the exception.

code:
=====================================
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, None)
        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)
                break

        # 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):
        self.raise_exc(SystemExit())

if __name__ == "__main__":
    import time
    import sys

    i_am_active = False

    def f():
        global i_am_active
        i_am_active = True
        try:
            try:
                while True:
                    time.sleep(0.01)
            except IOError, ex:
                print "IOError handler"
            except TypeError, ex:
                print "TypeError handler"
                print "ex=", repr(ex)
                typ, val, tb = sys.exc_info()
                print "typ=", repr(typ)
                print "val=", repr(val)
                print "tb=", tb
        finally:
            i_am_active = False

    t1 = Thread(target = f)
    t1.start()
    time.sleep(1)
    t1.raise_exc(TypeError("blah blah"))
    while i_am_active:
        time.sleep(0.01)
    print "!! thread terminated"

output:
=====================================
TypeError handler
ex= None
typ= <exceptions.TypeError instance at 0x00C15D28>  # should be the type
val= None # should be the instance
tb= <traceback object at 0x00C159E0>
!! thread terminated

if i change:
t1.raise_exc(TypeError("blah blah"))

to:
t1.raise_exc(TypeError)

i get:
=====================================
TypeError handler
ex= <exceptions.TypeError instance at 0x00C159B8>
typ= <class exceptions.TypeError at 0x00B945A0>
val= <exceptions.TypeError instance at 0x00C159B8>
tb= <traceback object at 0x00C15D00>
!! thread terminated

but then of course i can't pass arguments to the exception



-tomer


More information about the Python-Dev mailing list