From: Mike C. F. <mcf...@ro...> - 2004-05-07 17:41:25
|
Patrick K. O'Brien wrote: >"Matias Guijarro" <gui...@es...> writes: > > ... >>Reading source code, I did not notice any >>special code for dealing with this kind of >>usage. I expected to see some Queues, >>or Locks, etc. >> >>However I tried to use it with threads, and >>for my particular example it worked... As >>it is said to be a 'multi-producer, multi- >>consumer signals dispatcher', I think it is >>thread-safe ? >> >> Thread-safe is a matter of degree, a judgement based on experience that there are no places in the code where a thread deadlock/contention or unwanted interaction could occur. I've just done a quick audit of the core dispatch.py module, and from what I can see, it's *almost* thread-safe (which is the most dangerous type of thread-safe ;) ). >>If so, how it is achieved ? >>I would like to understand :-) >> >> Defensive coding, in this particular case, rather than queues and locks, the code uses try: except: blocks throughout to catch the cases where another thread has changed the situation. I think Patrick probably put them in to deal with synchronous changes (changes in the same thread), but they have the general effect of guarding against multi-thread problems as well. The results may not be what you'd expect (receivers you expected to be called (because you looked at them before calling send) aren't because they've been deleted by some other thread, for instance), but generally the code would get through the situation, and if you think about it, those are actually predictable results when you consider the nature of threading. There are a number of other things that one has to look out for, however; such as structures being replaced by processed versions of themselves (we seem pretty safe there), value checks where the value used to make the decision changes while working on the assumption, and objects mutating while you have a reference to them (e.g. by removing a value) which is a more general case of the previous problem. Recognise that last one :) ? PyDispatcher actually uses receivers.remove(receiver) to remove items from internal lists. If this is done while another thread is iterating over the list (there's no copy taken, in fact we use generators explicitly to avoid making such a copy for performance reasons), the result will be the iterating thread will miss a receiver. That's a fairly rare situation, so you're not going to see it in a few test runs, but it's definitely going to happen some day. There's a similar potential problem with _cleanupConnections, where the "if not receivers:" check could happen before another thread inserts a receiver, but the deletion occurs afterwards, resulting in a lost receiver (table) again. >>Thanks in advance for your answer! >> >> > >The original was not designed to be thread-safe. I'm not sure if Mike >changed anything in the current version to make it so, but I doubt >it. I don't know too much about threading with Python, so I'm not >sure how hard it would be to make the dispatching thread-safe. If it >could be done rather simply and elegantly I'm sure we'd love to have a >patch that adds this feature. > > The changes to increase the reliability of the code in the face of deletions shouldn't be too significant, but honestly I can't see myself getting a chance to work on it in the next few weeks (just too many other projects in the queue that need what little free time I can spare). I'd be concerned that we not kill performance, incidentally, as using locks can be very expensive for single-threading apps. One of the most useful things that someone could do would be to write test cases that *break* the existing model in the face of multiple threads (without worrying about fixing them). Have fun all, Mike _______________________________________ Mike C. Fletcher Designer, VR Plumber, Coder http://members.rogers.com/mcfletch/ |