WCF ReliableSession: Handle leak in Framework 4.5?

607 views Asked by At

After installing the 4.5 runtime on some servers, we experienced many winsock-related errors. I tracked the issue down to a handle leak to \Device\Afd, when using WCF with ReliableSessions on .Net 4.5 runtime (not reproducible on 4.0).

I wrote a test program to isolate/proof the problem.

Assume a pretty dummy WCF-service, and Client (as could be generated from VS):

[ServiceContract]
interface IMyService
{
    [OperationContract]
    void foo();
}

class MyService : IMyService
{
    public void foo() { }
}

//simulate a svcutil/VS-generated service reference
class MyServiceClient : ClientBase<IMyService>
{
    public MyServiceClient(Binding binding, EndpointAddress remoteAddress)
        : base(binding, remoteAddress) { }
}

And It's hosting and consuming.

class Program
{
    static void Main(string[] args)
    {
        NetTcpBinding binding = new NetTcpBinding(SecurityMode.None,false);
        EndpointAddress address = new EndpointAddress("net.tcp://localhost:6660");

        //Warmup (to stabilze handles for threads etc...)
        PerformTest(binding, address, 1);

        //Try without ReliableSession
        binding.ReliableSession.Enabled = false;
        PerformTest(binding, address, 1000);

        //Try with ReliableSession
        binding.ReliableSession.Enabled = true;
        PerformTest(binding, address, 1000);
    }

    private static void PerformTest(NetTcpBinding binding, EndpointAddress address, int nbConnections)
    {

        int nbHandlesBefore = Process.GetCurrentProcess().HandleCount;

        //Create & open Service
        var host = new ServiceHost(typeof(MyService));
        host.AddServiceEndpoint(typeof(IMyService), binding, address.Uri);
        host.Open();

        //Connect/Disconnect many times
        for (int i = 1; i <= nbConnections; i++)
        {
            using (var proxy = new MyServiceClient(binding, address))
            {
                proxy.Open();
                proxy.Close();
            }
            Console.Write("\r {0}/{1}    ", i, nbConnections);
        }

        host.Close();

        int nbHandlesAfter = Process.GetCurrentProcess().HandleCount;
        Console.WriteLine("Handle increase: {0}", nbHandlesAfter - nbHandlesBefore);
    }
}

The output of the test program is:

1/1    Handle increase: 144
1000/1000    Handle increase: -25
1000/1000    Handle increase: 1739

Remarks:

  • I know I should do better error handling because Close() can throw, but in this case it doesn't, so I prefered pasting less code here.
  • Here, I log the handle count for all handles (not only \Device\Afd). This makes it easier to test quickly, but I did many checks with sysinternals' handle.exe to verify the Name/Type of the handles
  • Handles are leaked by the Client, not the service (at least they are cleaned up properly after closing the servicehost).

Variations:

  • When changing the Proxy.Close() into Abort(), all handles in the are released properly (however the service has a hard time closing - as could be expected)
  • Creating the channels by IClientChannel proxy = ChannelFactory<IMyService>.CreateChannel(binding, address) as IClientChannel gives the same results
  • handles are released properly when creating channels through a channelfactory instance: IClientChannel proxy = new ChannelFactory<IMyService>(binding, address).CreateChannel() as IClientChannel
  • setting the ClientBase<>.CacheSetting to AlwaysOn, fixes the problem but is not always possible.

Does anyone know why this program is leaking handles?

0

There are 0 answers