We were getting a InvalidOperationException on a collection being modified during one of the foreach loops in
CleanupList(). It was on the line that says
foreach (WeakActionAndToken item in list.Value)
The situation was basically that we were doing a lot (couple of thousands) of register's very rapidly. We were also doing a bunch of send's during those register's. Our system was generally being hammered by a lot of processing, as this was a
stress test that we were doing. We did see that there was a lock in
CleanupList() on the lists object. The scope of the lock that was already there was on everything in the method except for the if (lists == null) check. We did not see a corresponding lock on an equivalent
object to "lists" in the Register() method. There is a lock on the
_registerLock object, but the equivalent of the "lists" object, which in
Register() is "recipients" (a reference to
_recipientsStrictAction or _recipientsOfSubclassesAction) does not have a lock. Perhaps I am misunderstanding what is going on here, but to me this seemed to be an issue. We ended up putting a lock on the "recipients"
object as was suggested by dmehers to encompass all of the code in Register()
after "recipients" is set. This seemed to correct our initial issue. The reason I wrote my first message was because we were applying the lock incorrectly (I think). I don't remember how we were locking in
Register(), but the lock I explained earlier seems to have fixed our issue.
But then we ran into another exception. We got another InvalidOperationException on a collection being modified, but this time it was in the
SendToList() method. This was on the line that says
foreach (WeakActionAndToken item in list)
This seemed to be occurring when SendToList() is called based on _recipientsStrictAction. We don't use any messaging that includes the subclasses, so it may be an issue there as well but we didn't see
it through our tests. What we ended up doing to fix this was removing the
listClone in SendToList(). Instead, we ended up putting locks around list = _recipientsOfSubclassesAction[type]; and
List<WeakActionAndToken> list = _recipientsStrictAction[messageType]; in the
SendToTargetOrType() method by locking the _recipientsOfSubclassesAction and
_recipientsStrictAction objects respectively. Inside of those locks, we performed the list cloning using
Take() to replace the listClone that we removed from
I hope this makes sense and that it doesn't introduce other errors. Based on our testing over the last couple days, these two fixes corrected any exceptions that we had been seeing. If it would help that I send you our version of the Messenger
class, please let me know. I guess I could also post the two methods (Register()
and SendToTargetOrType()) that we changed in its entirety... I will try to remember to do that.
I hope this helps. MVVM Light has helped us out immensely, and we owe a lot to the community and to Laurent in particular. Thanks!