title: Deadlock Risk in Nested Parallel For
body: <p>Take the following naive implementation of a nested async loop using the ThreadPool:</p>
  
  <pre><code>ThreadPool.SetMaxThreads(10, 10);
  CountdownEvent icnt = new CountdownEvent(1);
  for (int i = 0; i &lt; 50; i++)
  {
      icnt.AddCount();
      ThreadPool.QueueUserWorkItem((inum) =&gt;
      {
          Console.WriteLine("i" + inum + " scheduled...");
          Thread.Sleep(10000);  // simulated i/o
          CountdownEvent jcnt = new CountdownEvent(1);
          for (int j = 0; j &lt; 50; j++)
          {
              jcnt.AddCount();
              ThreadPool.QueueUserWorkItem((jnum) =&gt;
              {
                  Console.WriteLine("j" + jnum + " scheduled...");
                  Thread.Sleep(20000);  // simulated i/o
                  jcnt.Signal();
                  Console.WriteLine("j" + jnum + " complete.");
              }, j);
          }
          jcnt.Signal();
          jcnt.Wait();
          icnt.Signal();
          Console.WriteLine("i" + inum + " complete.");
      }, i);
  }
  icnt.Signal();
  icnt.Wait();
  </code></pre>
  
  <p>Now, you'd never use this pattern (it will deadlock on start) but it does demonstrate a specific deadlock you can cause with the threadpool - by blocking while waiting for nested threads to complete after the blocking threads have consumed the entire pool.</p>
  
  <p>I'm wondering if there's any potential risk of generating similarly detrimental behavior using the nested Parallel.For version of this:</p>
  
  <pre><code>Parallel.For(1, 50, (i) =&gt;
  {
      Console.WriteLine("i" + i + " scheduled...");
      Thread.Sleep(10000);  // simulated i/o
      Parallel.For(1, 5, (j) =&gt;
      {
          Thread.Sleep(20000);  // simulated i/o
          Console.WriteLine("j" + j + " complete.");
      });
      Console.WriteLine("i" + i + " complete.");
  });
  </code></pre>
  
  <p>Obviously the scheduling mechanism is far more sophisticated (and I haven't seen this version to deadlock at all), but the underlying risk seems like it may still be lurking there.  Is it theoretically possible to dry up the pool that the Parallel.For uses to the point of creating deadlock by having dependencies on nested threads? i.e. is there a limit to the number of threads that the Parallel.For keeps in it's back pocket for jobs that are scheduled after a delay?</p>