2

This is possibly related to ProgressBar updates in blocked UI thread but is a little different.

While troubleshooting a crashing WinForms control (a DevExpress treelist) a member of our team encountered an unusual situation, and I'm wondering if anyone can help us understand what's going on. English is not his first language, so I'm posting on his behalf.

Please see this screenshot from Visual Studio 2005.

Note the following points:

  1. The main UI thread is stopped and is currently in a DevExpress control draw method.

  2. The code shown on screen is from a point earlier in the same call-stack. This code is in the data layer and was called in response to the control's request for an image to display for the tree node. (perhaps also originating from a Paint handler)

  3. The displayed code is from earlier in the callstack, on the main UI thread, and is currently waiting on a lock! Since remote systems can send events which are processed on background threads in the data model (i.e., data models are sync'd between client and servers), we lock to keep the data collections thread safe.

  4. As the callstack shows, we continued to process paint messages on the UI thread, while we would expect the thread to be blocked.

This is very difficult to replicate, and I have not been able to do so using a simpler test project on my own box. When this situation arises, however, the result is that the DevExpress control's internal state can be messed up, causing the control to crash. This doesn't really seem like a bug in the control, since it was no doubt written with the assumption that these paint methods are running only on the UI thread. What we see here makes it look like the UI thread is acting like two threads.

It would seem possible that this is merely a Visual Studio bug in the presentation of the callstack, except that this whole endeavor is resulting from an effort to troubleshoot the occasional crash of the control in the released app (in which case it shows as a big red X in the UI), so it seems the problem is not isolated to the debug environment.

Alright, that was complicated, but hopefully made sense. Any ideas?

Community
  • 1
  • 1
dhochee
  • 412
  • 4
  • 11
  • I would love an answer to this as well, because I have never been able to get DevExpress to behave well WRT to images unless I only ever touched them on the UI thread. – Yaur May 27 '11 at 22:04
  • Could you put in some Debug.WriteLine(Thread.Name) or something at various places including right before the lock to be sure that the calls are happening on the same thread as the callstack seems to show? – BrandonAGr May 27 '11 at 22:21
  • The call stack, code, and thread location do seem to contradict each other. VS 2008 is not well regarded for debugging multi-threaded apps. Is there a chance there are multiple App Domains involved in this code? – hemp May 27 '11 at 22:21
  • @Yaur: Well, I don't recommend touching the DevX control except on the UI thread. – dhochee May 28 '11 at 05:34
  • @hemp: Definitely only one AppDomain. I'm not totally convinced the problem that's causing the control crash isn't unrelated, and that what we're seeing here is a Vis Studio bug, but it does seem to be the same issue. – dhochee May 28 '11 at 05:35
  • @BrandonAGr: Good idea. I know he has some Debug statements confirming that the order of events matches what we see, but including the Thread ID or name could help verify that what we are seeing is real. – dhochee May 28 '11 at 05:38
  • @dhochee not the controls, but the images. From what I've seen the "red x of doom" means that devexpress is modifying an image while you are also modifying it on a background thread. – Yaur May 28 '11 at 09:24
  • @Yaur: I see. Would be nice if they protected themselves against failed external dependencies a little better with try/catch and retry, instead of just dying. I think it's a little different than what we're experiencing. Hard to say. – dhochee May 28 '11 at 15:11

3 Answers3

3

I would strongly recommend against locking the UI to wait for background processing. Consider something like multiple buffering. You can probably get this behavior fairly easily by utilizing thread-safe collections in .NET 4, but if that's not an option there are versions of those in the Parallel Extensions released prior to v4.

hemp
  • 5,602
  • 29
  • 43
  • That we have locking in the data layer to ensure thread safety is not the problem here, and not what this post is about. I agree that multiple buffering and the new and improved thread safe collections that use spinning rather than locking are great, but that doesn't explain the behavior I'm observing. – dhochee May 28 '11 at 05:24
  • Clarification: The locking in the data layer IS the problem, so you're suggestion is good. It's not the problem I'm asking about tho. :) – dhochee May 28 '11 at 05:40
0

What about altering the synchronization scheme so that you don't need to acquire exclusive locks to read data?

In situations where you can be sure that a read will always produce consistent data even when it happens while the data is also being written, you might be able to get away with having no lock statements for getters. Otherwise there's ReaderWriterLockSlim, which permits multiple concurrent readers but still allows you to stop the presses for write operations.

It doesn't fix everything, but it does at least reduce the number of opportunities for deadlocks.

Sean U
  • 6,730
  • 1
  • 24
  • 43
  • As with hemp's advice, this is good, but doesn't really address the question at hand. I had already suggested to the developer that the lock on the getter is unnecessary (it was not part of the original design, and I'm not sure why it was added later, but I'm sure he had a reason). ReaderWriterLockSlim (which I've used before) might be a good approach without requiring v4, and I'll recommend that if he can't solve this, but I'd still really like to know what's going on because what I'm seeing in his screenshot defies my understanding of how threading is supposed to work. – dhochee May 28 '11 at 05:31
0

We see something similar in our project. The stack trace looks like the pump's event loop is called while the UI thread is waiting on a lock. This could happen if Monitor.enter has some special behavior when called on the UI thread. I believe this is what's happening, but I haven't found any documentation to back it up yet.

Probably has something to do with synchronization contexts :)

Mark Schaller
  • 23
  • 1
  • 3
  • Yep - it pumps the message queue while waiting. I am trying to find an internet reference for this since only Jow Duffy's Concurrent Programming on Windows is my only reference. – Luke Puplett Aug 19 '11 at 12:12
  • "Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping" -- on Thread.Join MSDN page, I know they use the same wait type. – Luke Puplett Aug 19 '11 at 12:30
  • 3
    Here's an internet reference (at long last!): At the end of this article, they say this: “The locking mechanism of the common language runtime (CLR) doesn’t behave exactly as one might imagine; one might expect a thread to cease operation completely when requesting a lock. In actuality, the thread continues to receive and process high-priority messages.” http://msdn.microsoft.com/en-us/library/ms741870.aspx – Mark Schaller Dec 28 '11 at 18:39