VB.NET in XSP

Support for compiling ASP.NET pages in other languages than C# has been a missing feature in mono ASP.NET classes for a long time. Not any more. Since a few minutes ago, we support any language that targets IL and has a CodeDomProvider.

I also added a VBCodeCompiler contributed by Jochen Wezel which makes our code generation for VB.NET work. The next step is to fix the issues raised in this mail, which look like minor problems in mbas, the mono VB.NET compiler.

And the big question now is: will Jackson ever contribute an ilasm CodeDomProvider?...

Following the thread

We all do it. We end up using the ThreadPool either directly or when invoking a delegate asynchronously.

A few days ago, while testing a patch that makes monologue download all the sindicated files asynchronously we found what appeared to be a bug but is in turn a feature.

Let's see some code that shows up the problem:

using System;
using System.Threading;

class TPTest
{
     static int count;
     delegate void DummyDelegate ();
     
     static void Main ()
     {
          for (int i = 0; i < 50; i++)
               ThreadPool.QueueUserWorkItem (new WaitCallback (Begin));

          while (true) {
               Thread.Sleep (500);
               int done = Interlocked.CompareExchange (ref count, 0, -1);
               Console.WriteLine ("{0} finished.", done);
               if (done == 50) {
                    Console.WriteLine ("DONE!");
                    break;
               }
          }
     }

     static void Begin (object unused)
     {
          DummyDelegate d = new DummyDelegate (SomethingElse);
          IAsyncResult ares = d.BeginInvoke (nullnull);
          d.EndInvoke (ares);
          Interlocked.Increment (ref count);
     }

     static void SomethingElse ()
     {
          Thread.Sleep (5000);
     }
}

It doesn't look like something that could hang... until you run it.

What happens in there?

  1. The for loop adds 50 tasks to the ThreadPool.
  2. Each of those tasks create a delegate and run BeginInvoke.
  3. BeginInvoke also uses the ThreadPool so more tasks are queued.
  4. EndInvoke waits indefinitely

The default number of threads in the ThreadPool is 25 per processor. You can check this out with:

     int max_threads, max_completion_ports;
     ThreadPool.GetMaxThreads (out max_threads, out max_completion_ports);

The tasks queued in step 3 are never executed because the ThreadPool queue is already full since step 1. This is what makes EndInvoke wait indefinitely, which in turn keeps the thread busy thus preventing the queue from shrinking... We got a deadlock!

If you want to see the number of available threads in the ThreadPool going down, just add these 3 lines before BeginInvoke:

     int threads, completion_ports;
     ThreadPool.GetAvailableThreads (out threads, out completion_ports);
     Console.WriteLine ("Available threads: {0}", threads);

The conclusion is that we should avoid waiting for asynchronous calls in a thread belonging to the ThreadPool. Or at least do it discretely...

This is a personal web page. Things said here do not represent the position of my employer.