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> </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> </p>
<p> </p>
<p>using System;</p>
<p>using System.Collections.Generic;</p>
<p>using System.Threading;</p>
<p>using Microsoft.Practices.Unity;</p>
<p> </p>
<p>namespace TestUnityProblem</p>
<p>{</p>
<p> public class Factory<T> where T : new()</p>
<p> {</p>
<p> public T GetInstance()</p>
<p> {</p>
<p> return new T();</p>
<p> }</p>
<p> }</p>
<p> </p>
<p> class SomeClass { }</p>
<p> </p>
<p> class Program</p>
<p> {</p>
<p> static void Main(string[] args)</p>
<p> {</p>
<p> int threadCount = 10;</p>
<p> </p>
<p> while (true)</p>
<p> {</p>
<p> var unityContainer = new UnityContainer();</p>
<p> unityContainer.RegisterType(typeof(Factory<>), new ContainerControlledLifetimeManager());</p>
<p> </p>
<p> var startEvent = new ManualResetEvent(false);</p>
<p> var threads = new List<Thread>();</p>
<p> </p>
<p> for (int i = 0; i < threadCount; i++)</p>
<p> {</p>
<p> threads.Add(new Thread(() =></p>
<p> {</p>
<p> startEvent.WaitOne();</p>
<p> Factory<SomeClass> factory = unityContainer.Resolve<Factory<SomeClass>>();</p>
<p> }</p>
<p> ));</p>
<p> }</p>
<p> </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> </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> </p>
<p> // Worked ok this time - go around the loop and try again</p>
<p> Console.Write(".");</p>
<p> }</p>
<p> }</p>
<p> }</p>
<p>}</p>