title: .NET AppDomain.Unload triggers runaway threads
body: <p>I have an .NET 3.5 SP1 application that is an Excel Add-in. The application is split into a parent <code>AppDomain</code> (Excel's) and a child domain in which we load all of our dlls. When we wish to update our application, we unload the child domain, replace the files and reload it. </p>
  
  <p>Unfortunately unloading the domain will activate 2 worker threads and they will start consuming CPU cycles (20-40%).</p>
  
  <p>If I debug with VS 2010, at the moment before and after the <code>AppDomain.Unload</code>, there are no threads alive with a call stack aside from Excel's main thread. The <code>AppDomain.Unload</code> is indeed unloaded because if I try to call Unload again I get an <code>AppDomainUnloadedException</code>.</p>
  
  <p>If I use ProcessExplorer, I can see that 2 threads are busy working away, even when the VS debugger has breaked. Looking at the callstack reveals nothing because there are no symbols.</p>
  
  <ul>
  <li>ntkrnlpa.exe+0x6eacb </li>
  <li>ntkrnlpa.exe+0x2bfd0 </li>
  <li>hal.dll+0x2ef2</li>
  <li>ntkrnlpa.exe+0x6a6cf </li>
  <li>ntdll.dll+0xe514 </li>
  <li>mscorwks.dll+0x992d</li>
  <li>mscorwks.dll+0x52568 </li>
  <li>mscorwks.dll+0x15b469 </li>
  <li>kernel32.dll+0xb729</li>
  </ul>
  
  <p>If I use WinDbg, I can see the callstack for the 2 renegade threads. It's always the same thing:</p>
  
  <ul>
  <li>WARNING: Stack unwind information not available. Following frames may be wrong.</li>
  <li>ntdll!KiFastSystemCallRet</li>
  <li>mscorwks+0x992d</li>
  <li>mscorwks!InstallCustomModule+0x1eca0</li>
  <li>mscorwks!CorExitProcess+0x503b</li>
  <li>kernel32!GetModuleFileNameA+0x1ba </li>
  </ul>
  
  <p>I created a very simple test application to load/unload a child assembly. When doing this with a simple 1-class assembly, it works without any problems. If I get it to load/unload the child domain of the real application, it triggers the same renegade threads.</p>
  
  <p>The code that creates the child domain is as follows:</p>
  
  <pre><code>AppDomainSetup appSetup = new AppDomainSetup();
  appSetup.ApplicationBase = baseDir;
  
  var ps = new PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
  return AppDomain.CreateDomain(name, null, appSetup, ps, null);
  </code></pre>
  
  <p>The communication from the parent to the child domain is via a proxy and reflection. The code to create it is below:</p>
  
  <pre><code>string assName = typeof(ApplicationProxy).Assembly.FullName;
  string className = typeof(ApplicationProxy).FullName;
  
  var obj = _childDomain.CreateInstanceAndUnwrap(assName, className, false, 
      System.Reflection.BindingFlags.Default,
      null, new object[]{_sessionGuid}, 
      CultureInfo.InvariantCulture,
      null, new Evidence(AppDomain.CurrentDomain.Evidence));
  
  _proxy = (ApplicationProxy)obj;
  </code></pre>
  
  <p>I've googled the problem profusely and cannot find anybody with a similar problem. The application is 10 projects large so I can't post it.</p>
  
  <p>I'm wondering if anybody has encountered something similar and has some tips for me. Otherwise does anybody have any thoughts on how to attack the problem?</p>