From 2a20e0994440aaa344ca9a3cf19fccd281707193 Mon Sep 17 00:00:00 2001 From: baloan Date: Mon, 15 Nov 2010 22:28:35 +0100 Subject: [PATCH] thread termination --HG-- branch : aspn --- .project | 17 ++++++++++ .pydevproject | 10 ++++++ src/killable_thread.py | 74 +++++++++++++++++++++++++++++++++++++++++ src/stoppable_thread.py | 25 ++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 .project create mode 100644 .pydevproject create mode 100644 src/killable_thread.py create mode 100644 src/stoppable_thread.py diff --git a/.project b/.project new file mode 100644 index 0000000..f52f07b --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + aspn + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 0000000..9621a0d --- /dev/null +++ b/.pydevproject @@ -0,0 +1,10 @@ + + + + +Default +python 2.6 + +/aspn/src + + diff --git a/src/killable_thread.py b/src/killable_thread.py new file mode 100644 index 0000000..b048767 --- /dev/null +++ b/src/killable_thread.py @@ -0,0 +1,74 @@ +# http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python + +''' KillableThread ''' + +# system imports + +import ctypes +import inspect +import threading + +# definitions + +def _async_raise(tid, exctype): + '''Raises an exception in the threads with id tid''' + if not inspect.isclass(exctype): + raise TypeError("Only types can be raised (not instances)") + res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) + if res == 0: + raise ValueError("invalid 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 ThreadWithExc(threading.Thread): + '''A thread class that supports raising exception in the thread from another thread. + ''' + def _get_my_tid(self): + """determines this (self's) thread id + + CAREFUL : this function is executed in the context of the caller thread, + to get the identity of the thread represented by this instance. + """ + if not self.isAlive(): + raise threading.ThreadError("the thread is not active") + + # do we have it cached? + if hasattr(self, "_thread_id"): + return self._thread_id + + # no, look for it in the _active dict + for tid, tobj in threading._active.items(): + if tobj is self: + self._thread_id = tid + return tid + + # TODO : in python 2.6, there's a simpler way to do : self.ident ... + + raise AssertionError("could not determine the thread's id") + + def raiseExc(self, exctype): + """Raises the given exception type in the context of this thread. + + If the thread is busy in a system call (time.sleep(), socket.accept(), ...) the exception + is simply ignored. + + If you are sure that your exception should terminate the thread, one way to ensure that + it works is: + t = ThreadWithExc( ... ) + ... + t.raiseExc( SomeException ) + while t.isAlive(): + time.sleep( 0.1 ) + t.raiseExc( SomeException ) + + If the exception is to be caught by the thread, you need a way to check that your + thread has caught it. + + CAREFUL : this function is executed in the context of the caller thread, + to raise an excpetion in the context of the thread represented by this instance. + """ + _async_raise( self._get_my_tid(), exctype ) diff --git a/src/stoppable_thread.py b/src/stoppable_thread.py new file mode 100644 index 0000000..96ef9ba --- /dev/null +++ b/src/stoppable_thread.py @@ -0,0 +1,25 @@ +# http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python + +''' StoppableThread ''' + +# system imports + +from threading import Event, Thread + +# definitions + +class StoppableThread(Thread): + """Thread class with a stop() method. The thread itself has to check + regularly for the stopped() condition.""" + + def __init__(self): + super(StoppableThread, self).__init__() + self._stop = Event() + + def stop(self): + self._stop.set() + + def stopped(self): + return self._stop.isSet() + +