Freigeben über


Nested message loops, part 2

As we get closer to shipping WPF, I get more and more wary of what I call heavy-handed fixes.  Places where in order to fix a corner case, we disallow another case that works perfectly well.  One example is a bug we found recently, where if you do a nested message loop in the middle of processing the IsMouseOver property change, you potentially receive the MouseLeave before the MouseEnter.  Because both MouseEnter and the IsMouseOver property are tied to one window message, but because of that nested message loop, we don't finish processing that message before we see the message for the MouseLeave.  We could fix this by just not letting you put a nested message loop there, but that would be punishing all the people who don't care about MouseEnter/Leave in this scenario.

Actually, this issue isn't really specific to nested message loops, they just make it worse.  It's really an example of a more general problem with synchronous events, that there is no causal ordering.  Synchronous events mean that when the event happens, you process all the event handlers immediately -- you don't wait until some later time to call them.  The problem is that event handlers can beget other events, and when you have multiple handlers listening, they can hear about events in a different order from when they were fired.  Take a simple example where you have a single object with a single property, object.Property, and a pair of event handlers A and B listening to the property change.  And suppose that A changes that same property.  Consider what happens:

// default value of object.Property is 0

object.Property = 1; // initial action

call A w/ old = 0, new = 1

  A does: object.Property = 2;

  call A w/ old = 1, new = 2

  call B w/ old = 1, new = 2

 call B w/ old = 0, new = 1

So B hears about A's changes before it hears about what A was responding to.

When you simplify these examples down, they start to seem pathological, but this sort of stuff happens in real code.  Unfortunately, there's not a whole lot we can do about it.  Very early in WPF, we tried to use asynchronous events, where we would post all events to the message queue rather than process event handlers immediately.  Unfortunately, that really made life difficult in 99.9% case where you didn't have causal ordering problems -- because now there's a window between when you changed something and when the rest of the world caught up with the change.  Which often forced you to post a work item of your own, which became a real pain to write and even more painful to debug.  In order to fix a rare problem, we were forcing average developers doing the most basic scenarios to use advanced techniques.  In other words, a very heavy-handed solution.

Comments

  • Anonymous
    May 09, 2006
    And this, of course, is going straight into the documentation for WPF, right?
  • Anonymous
    May 09, 2006
    Oluf, I don't see why it would. It's not a problem with WPF, specifically. In fact, it's not really a problem at all - just a side-effect of how synchronous events work - and is common with any .NET framework that makes use of events.
  • Anonymous
    May 10, 2006
    No, I don't expect we'll put this in the official documentation.  It's just another time in the day thing -- putting something in the official documentation takes longer than writing a blog entry, and given a limited number of hours in the day, I want us to spend our time documenting the things that will make the biggest difference to the most people.  I want to do the best possible job helping people learn to use WPF, rather than narrowly focusing on the official documentation at the expense of books, articles, blogs, newsgroups, etc. -- and I appreciate your feedback on how we can do that better.  Thanks.