How to force a thread to stop

Jean-Paul Calderone exarkun at divmod.com
Mon Jul 24 17:49:49 EDT 2006


On Mon, 24 Jul 2006 13:51:07 -0700, "Carl J. Van Arsdall" <cvanarsdall at mvista.com> wrote:
>Jean-Paul Calderone wrote:
>> On Mon, 24 Jul 2006 11:22:49 -0700, "Carl J. Van Arsdall" <cvanarsdall at mvista.com> wrote:
>>
>>> Steve Holden wrote:
>>>
>>>> Carl J. Van Arsdall wrote:
>>>> [... rant ...]
>>>>
>>>>
>>>>> So with this whole "hey mr. nice thread, please die for me" concept gets
>>>>> ugly quickly in complex situations and doesn't scale well at all.
>>>>> Furthermore, say you have a complex systems where users can write
>>>>> pluggable modules.  IF a module gets stuck inside of some screwed up
>>>>> loop and is unable to poll for messages there's no way to kill the
>>>>> module without killing the whole system.  Any of you guys thought of a
>>>>> way around this scenario?
>>>>>
>>>>>
>>>>>
>>>>>
>>>> Communications through Queue.Queue objects can help. But if you research
>>>> the history of this design decision in the language you should discover
>>>> there are fairly sound rasons for not allowing arbitrary "threadicide".
>>>>
>>>>
>>>>
>>>>
>>> Right, I'm wondering if there was a way to make an interrupt driven
>>> communication mechanism for threads?  Example: thread receives a
>>> message, stops everything, and processes the message.
>>>
>>>
>>
>> And what happens if the thread was halfway through a malloc call and
>> the data structures used to manage the state of the heap are in an
>> inconsistent state when the interrupt occurs?
>>
>> This has been discussed many many times in the context of many many
>> languages and threading libraries.  If you're really interested, do
>> the investigation Steve suggested.  You'll find plenty of material.
>>
>
>I've been digging around with Queue.Queue and have yet to come across
>any solution to this problem.

Right.  Queue.Queue doesn't even try to solve this problem.

>Queue.Queue just offers a pretty package 
>for passing around data, it doesn't solve the "polling" problem.

Exactly correct.

>I 
>wonder why malloc()'s can't be done in an atomic state (along with other
>operations that should be atomic, maybe that's a question for OS guys, I
>dunno).

(Note that malloc() is just a nice example - afaik it is threadsafe on
all systems which support threading at all)

Because it turns out that doing things atomically is difficult :)

It's not even always obvious what "atomic" means.  But it's not impossible
to do everything atomically (at least not provably ;), and in fact many
people try, most commonly by using mutexes and such.

However, unless one is extremely disciplined, critical sections generally
get overlooked.  It's often very difficult to find and fix these, and in
fact much of the time no one even notices they're broken until long after
they have been written, since many bugs in this area only surface under
particular conditions (ie, particular environments or on particular
hardware or under heavy load).

>Using Queue.Queue still puts me in a horribly inflexible
>"polling" scenario.  Yea, I understand many of the reasons why we don't
>have "threadicide", and why it was even removed from java.  What I don't
>understand is why we can't come up with something a bit better.  Why
>couldn't a thread relinquish control when its safe to do so?  

Of course, if you think about it, CPython does exactly this already.  The
GIL ensures that, at least on the level of the interpreter itself, no
thread switching can occur while data structures are in an inconsistent
state.  This works pretty well, since it means that almost anything you do
at the application level in a multithreaded application won't cause random
memory corruption or other fatal errors.

So why can't you use this to implement killable threads in CPython?  As it
turns out, you can ;)  Recent versions of CPython include the function
PyThreadState_SetAsyncExc.  This function sets an exception in another
thread, which can be used as a primitive for killing other threads.

Why does everyone say killing threads is impossible, then?  Well, for one
thing, the CPython developers don't trust you to use SetAsyncExc correctly,
so it's not exposed to Python programs :)  You have to wrap it yourself
if you want to call it.  For another thing, the granularity of exceptions
being raised is somewhat sketchy: an exception will not be raised while
a single bytecode operation is being executed.  Exceptions set with this
function will only be raised /between/ the execution of two bytecode
operations.

But this is just what you described above.  The thread is checking for
messages at certain intervals but only while all of its internal state
is consistent.  The problem here is that the granularity of a bytecode
being executed is pretty variable.  Some operations might take only a
microsecond.  Other operations might take a week.  This might be useful
in some specific contexts, but it's definitely not a general solution.


>While the
>interpreter is busy doing malloc()s a thread receives a control message,
>the thread waits until it knows its no longer in an atomic state and
>gives control to the message handler when it can.  Its about setting up
>a large system that is controllable without the painstaking process of
>putting message polling loops all over the place.  Main threads in a
>python program can setup a signal handler, accept signals, and that
>signal can happily go ahead and kill a python interpreter.  Why can't
>this concept be taken farther and introduced into threading?

Some of your text in this paragraph is a little fuzzy, but I think I
get the general idea, and hopefully from what I've written above it is
clear both why what you describe above basically /is/ being done in
CPython and why it is not actually a general solution to this problem.

>
>There is no system that is completely interruptible, there will always
>be a state in which it is not safe to interrupt, but many systems work
>around this just fine with cautious programming.

Which systems work around this just fine?  I don't think there are very
many at all.  Pre-emptive threading is really hard to get right, even
though sometimes it /looks/ easy ;)

Jean-Paul



More information about the Python-list mailing list