Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This is a race condition in your code and not a bug in RhinoMocks. The problem occurs when you are setting up the allTasks task list in the <code>Start()</code> method:</p> <pre><code>public void Start() { var allTasks = new List&lt;Task&gt;(); foreach (var foo in _fooList) // the next line has a bug allTasks.Add(Task.Factory.StartNew(() =&gt; foo.DoSomething())); Task.WaitAll(allTasks.ToArray()); } </code></pre> <p>You need to pass the foo instance explicitly into the task. The task will execute on a different thread and it's very likely that the foreach loop will replace the value of foo before the task has started.</p> <p>This means that each <code>foo.DoSomething()</code> is being invoked sometimes never and sometimes more than once. For this reason, some of the tasks will block indefinitely because RhinoMocks can't handle overlapped raising of events on the same instance from different threads and it gets into a deadlock.</p> <p>Replace this line in your <code>Start</code> method:</p> <pre><code>allTasks.Add(Task.Factory.StartNew(() =&gt; foo.DoSomething())); </code></pre> <p>With this:</p> <pre><code>allTasks.Add(Task.Factory.StartNew(f =&gt; ((IFoo)f).DoSomething(), foo)); </code></pre> <p>This is a classic bug that is subtle and very easy to overlook. It is sometimes referred to as "accessing a modified closure".</p> <p>PS:</p> <p>Following the comments on this post, I rewrote this test using Moq. In this case it doesn't block - but beware that expectations created on a given instance might not be satisfied unless the original bug is fixed as described. GenerateFoo() using Moq looks like this:</p> <pre><code>private List&lt;IFoo&gt; GenerateFooList(int max) { var fooList = new List&lt;IFoo&gt;(); for (int i = 0; i &lt; max; i++) fooList.Add(GenerateFoo()); return fooList; } private IFoo GenerateFoo() { var foo = new Mock&lt;IFoo&gt;(); foo.Setup(f =&gt; f.DoSomething()).Raises(f =&gt; f.myEvent += null, EventArgs.Empty); return foo.Object; } </code></pre> <p>It's more elegant than RhinoMocks - and clearly more tolerant of multiple threads raising events on the same instance simultaneously. Although I don't imagine this is a common requirement - personally I don't often find scenarios where you can assume the subscribers to an event are thread-safe.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. CO+1 - yes that's a foreach closure problem (a temp variable would do too) - and you might have only one foo called 50 times - but does that 'yield' the deadlock? I'm not that familiar w/ Rhino (though mock is a mock:) but doesn't look like anything there blocks, explicitly at least (mock code). IMO you'd get 50 events fired on just one foo, but still looks set to end.
      singulars
    2. CODeadlock is on RhinoInterceptor.Intercept method which is marked with the SynchronizedAttribute, equivalent to a lock(this). Bad news once you've got multiple threads executing on the same instance. I haven't followed the whole chain of calls in the debugger step by step (I don't fancy reverse engineering RhinoMocks and Castle - that code is hairy!), but I spotted that calls to Intercept appear multiple times within a stack - so looks like a deadlock caused by two threads that have passed through Intercept once on different objects and are holding locks now trying to acquire each others'.
      singulars
    3. COthat makes more sense :), now it's clear - but it's still IMO the fault of the Rhino's design - as there is nothing in there that justifies the locks (though I can understand how one could get there easily) - i.e. if you handcraft the Mock (manual class) it should work w/o problems - if I'm not mistaken i.e. the bug with the closure is just bringing up the problems that lie under the hood ...while that could solve the locking (so it is an answer to the specific Q - but then again someone wanted this cleared related to Rhino seems) - you'd get trapped soon again. I too didn't debug so...
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload