How can I lower the priority of Update Controls messages?

Developer
Dec 29, 2011 at 4:32 PM
Edited Dec 29, 2011 at 4:39 PM

I have a serious problem that I have been too clueless to solve so far. In my app, under certain conditions, updates coming in from the network (which modify Independents, in a routine running on the UI thread via Dispatcher.BeginInvoke()) cause the application to lock up and become totally unresponsive. I believe this is because updates initiated by UpdateControls.XAML have higher priority than painting and input messages, for some reason, and perhaps higher priority than BeginInvoke() (that is, if several network messages are queued up, perhaps all the UI updates are being computed, then a SINGLE network message is processed, then UC causes the UI updates to be computed again, then another network message is processed, and so forth, instead of deferring the UI updates until all the BeginInvoke() calls have been processed).

Basically, the app locks up until I terminate the network server or otherwise interrupt the connection.

In contrast, UpdateControls.Forms never locks up the application, since updates are processed in Application.Idle, and therefore have lower priority than everything else.

Can you think of any way to change the priorities in WPF so that the app cannot lock up?

Developer
Dec 29, 2011 at 5:48 PM
Edited Dec 29, 2011 at 5:55 PM

Hahahahaha! Ahh, I figured it out. It's so easy. We just need a one-line change in ObjectProperty, to add a priority parameter to the BeginInvoke which is called in response to the Dependent.Invalidated event. I set the priority to "Background" (one notch above "ContextIdle"), and while the app still seems a bit more sluggish in WPF than WinForms, an action that used to lock up the app no longer does.

I think this fix is very important so I'm checking it in.

Edit: DOH! I forgot to write a checkin comment again. Sorry.

Coordinator
Dec 30, 2011 at 4:53 PM

Cool. Thanks for the fix.

You're running up against a threading feature of UC. If an Independent changes while a Dependent that uses it is being updated, it will enter the UPDATING_AND_OUT_OF_DATE state. While this does keep the foreground from having to block the background thread, it is prone to the background thread starving the foreground.

In general, I think you can make the UI more responsive by throttling the assignments to Independents. Only use Independents for summaries or progress information, not for every detail. Update the progress summary based on a number of detail changes, or based on a timer.

Developer
Dec 30, 2011 at 6:30 PM
Edited Dec 30, 2011 at 6:36 PM

Actually I am processing everything on the UI thread except the network operations themselves. A message is received on the network thread, parsed, then BeginInvoke transfers control to the UI thread and the model (with all its Independents) is updated.

With most computers having multiple cores, it would be really nice if I could update the independents on a background thread and then update the most important dependents ALSO on the background thread, and somehow prevent the UI from asking for those dependents until they have been updated. But let's say I update the Independents on the network thread... there is nothing stopping the UI from updating the Dependents immediately, right?

Even if I get the thread synchronization right, I expect this will actually decrease performance because while one network message is being processed, the Dependents could be updated on the UI thread before the message is fully processed, and then more independents will be changed on the network thread and then the Dependents will be updated again. Even if I explicitly update the Dependents on the network thread, there's nothing stopping UC from requesting the value of those same Dependents from the UI thread at about the same time, which would make the UI thread block until the Dependent is updated (no matter which thread it is being updated on). Right?

Say, here's a simple solution. What if UpdateControls.XAML and UpdateControls.Forms recognized a way to suspend all UI updates? Something like:

// on the background/network thread
// (DelayGuiUpdates should be agnostic of the actual GUI technology)
using (var _ = new UpdateControls.DelayGuiUpdates()) { // - TODO: update the model
// - TODO: Do OnGet for important dependents
}

I imagine an optional parameter to DelayGuiUpdates() to indicate the maximum delay in milliseconds until an update (because if the update takes a long time, the GUI should not refuse to update indefinitely, especially considering that some independents can be changed by the user).

The UC GUI code could respond to the global delay flag by "spinning its wheels": when an Application.Idle event is fired (WinForms) or the background message is processed (WPF ObjectPropertyAtom), the event handler should check if the delay flag is set and, if so, post itself (that is, post the same event handler) to the UI message queue and return. The disadvantage of this simple approach is that it will keep the GUI thread at 100% CPU.

A more complicated, but better, approach would be that when the DelayGuiUpdates object is disposed, it raises a static event. Event handlers from UpdateControls.XAML and UpdateControls.Forms would subscribe to this event if the delay flag is set in the event handler, and then they use BeginInvoke() to repost themselves when the event fires. This approach doesn't waste CPU time, but with this design it is more difficult to support a time limit on the delay. (Incidentally, I wonder if multicast delegates become slow when the number of subscribers becomes large...)

What are your thoughts?