I'll take a stab at this...
You can take a look at the MVVM-Light source code on its CodePlex site. I'm going to paste in the relevant method here (slightly annotated for the sake of this post):
private void SendToTargetOrType<TMessage>(TMessage message, Type messageTargetType, object token)
{
var messageType = typeof(TMessage);
if (_recipientsOfSubclassesAction != null)
{
// Clone to protect from people registering in a "receive message" method
// Correction Messaging BL0008.002
var listClone =
_recipientsOfSubclassesAction.Keys.Take(_recipientsOfSubclassesAction.Count()).ToList();
foreach (var type in listClone)
{
List<WeakActionAndToken> list = null;
if (messageType == type
|| messageType.IsSubclassOf(type)
|| type.IsAssignableFrom(messageType))
{
lock (_recipientsOfSubclassesAction)
{
list = _recipientsOfSubclassesAction[type].Take(_recipientsOfSubclassesAction[type].Count()).ToList();
}
}
// Class A probably sends a message here from the UI thread
SendToList(message, list, messageTargetType, token);
}
}
if (_recipientsStrictAction != null)
{
// Class B grabs this lock on the background thread.
// Class A continues processing on the UI thread and arrives here.
// An attempt is made to grab the lock on the UI thread but it is
// blocked by the background thread & Class B which in turn is waiting
// on the UI thread. And here you have yourself a deadlock
lock (_recipientsStrictAction)
{
if (_recipientsStrictAction.ContainsKey(messageType))
{
var list = _recipientsStrictAction[messageType]
.Take(_recipientsStrictAction[messageType].Count())
.ToList();
// Class B sends its message here.
// Class C receives the message and does an Invoke on the UI thread
SendToList(message, list, messageTargetType, token);
}
}
}
RequestCleanup();
}
- Class A probably sends a message on the UI thread picked up by 'subclass recipients'.
- Class B is a recipient that picks up this message and kicks off your background task.
- Your background task then sends a message that has a 'strict action recipient'.
- Class B grabs the '_recipientsStrictAction' lock on the background thread.
- Class B sends the message to class C, which does an invoke on the UI thread.
- This invoke blocks because the UI thread is still executing the first message.
- UI thread execution continues on and then tries to grab the '_recipientsStrictAction' lock on the UI thread. Unfortunately, your background thread (which is waiting on the UI thread) already has the lock. You are now deadlocked :(
Might want to consider doing an InvokeAsync in Class C rather than an Invoke. I think you could probably avoid the issue that way.
Makes me wonder why MVVM light is sending the message 'inside' the lock. Seems like a not-so-cool sort of thing to do. After typing all this up, I went looking around the CodePlex site, looks like this is issue has been documented:
http://mvvmlight.codeplex.com/workitem/7581