4
Vote

Resolve from unbound generic type registration can block in multi-threaded environment

description

<p>I hit this problem in production code, but I've been able to put together a standalone repro - see code below. I'm using Unity 1.2, but the problem seems to be the same in 2.0. The problem occurs when multiple threads call Resolve at the same time, for the same singleton (i.e. ContainerControlledLifetimeManager), when the singleton has not been resolved before. I believe the problem is specific to registration of unbound generic types. When the problem hits, one or more threads (usually one) block forever in SynchronizedLifetimeManager.GetValue. Subsequent Resolve calls for the same type work fine.</p> <p>&#160;</p> <p>The code below demonstrates the problem. If working correctly, it should just print a continuous line of dots to the console indefinitely. However on my system it hangs after a few dots. You may need to tweak the threadCount value - generally higher is better for reproducing the problem.</p> <p>&#160;</p> <p>&#160;</p> <p>using System;</p> <p>using System.Collections.Generic;</p> <p>using System.Threading;</p> <p>using Microsoft.Practices.Unity;</p> <p>&#160;</p> <p>namespace TestUnityProblem</p> <p>{</p> <p> public class Factory&lt;T&gt; where T : new()</p> <p> {</p> <p> public T GetInstance()</p> <p> {</p> <p> return new T();</p> <p> }</p> <p> }</p> <p>&#160;</p> <p> class SomeClass { }</p> <p>&#160;</p> <p> class Program</p> <p> {</p> <p> static void Main(string[] args)</p> <p> {</p> <p> int threadCount = 10;</p> <p>&#160;</p> <p> while (true)</p> <p> {</p> <p> var unityContainer = new UnityContainer();</p> <p> unityContainer.RegisterType(typeof(Factory&lt;&gt;), new ContainerControlledLifetimeManager());</p> <p>&#160;</p> <p> var startEvent = new ManualResetEvent(false);</p> <p> var threads = new List&lt;Thread&gt;();</p> <p>&#160;</p> <p> for (int i = 0; i &lt; threadCount; i++)</p> <p> {</p> <p> threads.Add(new Thread(() =&gt;</p> <p> {</p> <p> startEvent.WaitOne();</p> <p> Factory&lt;SomeClass&gt; factory = unityContainer.Resolve&lt;Factory&lt;SomeClass&gt;&gt;();</p> <p> }</p> <p> ));</p> <p> }</p> <p>&#160;</p> <p> foreach (Thread t in threads) { t.Start(); }</p> <p> Thread.Sleep(50); // Give the threads a chance to reach the event wait.</p> <p> startEvent.Set();</p> <p>&#160;</p> <p> // If we hit the problem, at least one of the threads will hang, which means t.Join will hang.</p> <p> foreach (Thread t in threads) { t.Join(); }</p> <p>&#160;</p> <p> // Worked ok this time - go around the loop and try again</p> <p> Console.Write(&quot;.&quot;);</p> <p> }</p> <p> }</p> <p> }</p> <p>}</p>

comments

andymcm wrote Jun 19, 2011 at 10:27 AM

Edited title and description to emphasize that the problem is specific to registrations of unbound generic types.

From debugging the code, it seems that resolves from unbound generic types result in an additional ContainerControlledLifetimeManager object being created. However in the multi-threaded scenario, multiple of these additional ContainerControlledLifetimeManager objects can get created, and the locking and unlocking of the SynchronizedLifetimeManager lock gets done across instances. So thread A locks instance X of ContainerControlledLifetimeManager, then thread B waits on instance X's lock, then thread A unlocks instance Y, so thread B is blocked forever waiting for instance X to be unlocked.