Block static object from delegate doesn't work
Why doesn't it work?
private static object Lock_HandleError = new object();
public static void HandleError(Exception ex)
{
lock( Lock_HandleError )
{
//IF the UI is processing a visual tree event (such as IsVisibleChanged), it throws an exception when showing a MessageBox as described here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/44962927-006e-4629-9aa3-100357861442
//The solution is to dispatch and queue the MessageBox. We must use BeginInvoke because dispatcher processing is suspended in such cases.
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate()
{
lock( Lock_HandleError )
{
Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate(){
HandleError(new Exception("testing purposes only"));
}, DispatcherPriority.Background);
MessageBox.Show(ex.Message, "Application Error", MessageBoxButton.OK, MessageBoxImage.Error);
//This point is not reached until the user clicks "OK"
}
}, DispatcherPriority.Background);
}
}
public void main()
{
HandleError(new Exception("The first error"));
}
The expected behavior of the above code is that there is one error message at a time, and when the user clicks OK, the object Lock_HandleError
will be freed from the posted stream, and the next HandleError call can continue - but what I get is an endless cascade of messages about errors without even hitting OK.
Why is this blocking not working?
By setting breakpoints on writing and exiting each blocking statement, I can clearly see that the delegate raises lock()
and dispatches a new call again in "HandleError" and then pauses the MessageBox to wait for user input.
Meanwhile, on a different thread, the dispatched HandleError call is triggered - but instead of waiting in the lock () statement, as you'd expect, it just explodes through it, even though the MessageBox delegate explicitly allocates the lock and hasn't released it yet.
source to share
The answer is in two parts:
-
Understand that locks come back. When a thread already holds a lock on an object, that thread can acquire the same lock over and over without blocking.
-
While the first MessageBox is up, the UI thread is still pumping messages, so subsequent (recursive) calls are not
HandleError
processed on the UI thread (which, since it already holds the lock, can be re-centered).
source to share
Why is this blocking not working?
A thread is allowed to enter a blocking statement that it already has. In essence, blocking does not block its own thread.
This way, what happens on the original thread takes a lock and then is allowed to add messages to the dispatcher queue. He can add as much as he wants.
The dispatcher receives the first message during processing and then raises a HandleError. Since it works on the dispatcher thread, it allows the injecting of outer and inner locks and raises HandleError again, recursively adding new messages to the queue in an infinite loop.
source to share