File: channels\ipc\portcache.cs
Project: ndp\clr\src\managedlibraries\remoting\System.Runtime.Remoting.csproj (System.Runtime.Remoting)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//===========================================================================
//  File:       PortCache.cs
//  Author:   Microsoft@Microsoft.Com
//  Summary:    Implements a cache for port handles
//
//==========================================================================
 
using System;
using System.Collections;
using System.Threading;
using System.Security.Principal;
 
namespace System.Runtime.Remoting.Channels.Ipc
{
    internal class PortConnection
    {
        private IpcPort _port;
        private DateTime _socketLastUsed;
 
        internal PortConnection(IpcPort port)
        {
            _port = port;
            _socketLastUsed = DateTime.Now;
        }
 
        internal IpcPort Port { get { return _port; } }
        internal DateTime LastUsed { get { return _socketLastUsed; } }
    }
    
    internal class ConnectionCache
    {
        // collection of RemoteConnection's.
        private static Hashtable _connections = new Hashtable();
 
        // socket timeout data
        private static RegisteredWaitHandle _registeredWaitHandle;
        private static WaitOrTimerCallback _socketTimeoutDelegate;
        private static AutoResetEvent _socketTimeoutWaitHandle;
        private static TimeSpan _socketTimeoutPollTime = TimeSpan.FromSeconds(10);
        private static TimeSpan _portLifetime = TimeSpan.FromSeconds(10);
 
        static ConnectionCache()
        {
            InitializeConnectionTimeoutHandler();
        }
            
        private static void InitializeConnectionTimeoutHandler()
        {
            _socketTimeoutDelegate = new WaitOrTimerCallback(TimeoutConnections);
            _socketTimeoutWaitHandle = new AutoResetEvent(false);
            _registeredWaitHandle = 
                ThreadPool.UnsafeRegisterWaitForSingleObject(
                    _socketTimeoutWaitHandle, 
                    _socketTimeoutDelegate, 
                    "IpcConnectionTimeout", 
                    _socketTimeoutPollTime, 
                    true); // execute only once
        } // InitializeSocketTimeoutHandler
 
        private static void TimeoutConnections(Object state, Boolean wasSignalled)
        {
            DateTime currentTime = DateTime.UtcNow;
 
            lock (_connections)
            {
                foreach (DictionaryEntry entry in _connections)
                {
                    PortConnection connection = (PortConnection)entry.Value; 
                    if (DateTime.Now - connection.LastUsed > _portLifetime)
                        connection.Port.Dispose();
                }
            }
            
            _registeredWaitHandle.Unregister(null);
            _registeredWaitHandle =
                ThreadPool.UnsafeRegisterWaitForSingleObject(
                    _socketTimeoutWaitHandle, 
                    _socketTimeoutDelegate, 
                    "IpcConnectionTimeout", 
                    _socketTimeoutPollTime, 
                    true); // execute only once      
        } // TimeoutConnections
        
        // The key is expected to of the form portName
        public IpcPort GetConnection(String portName, bool secure, TokenImpersonationLevel level, int timeout)
        {
            PortConnection connection = null;
            lock (_connections)
            {
                bool cacheable = true;
                if (secure)
                {
                  try
                  {
                    WindowsIdentity currentId = WindowsIdentity.GetCurrent(true/*ifImpersonating*/);
                    if (currentId != null)
                    {
                      cacheable = false;
                      currentId.Dispose();
                    }
                  }
                  catch(Exception)
                  {
                      cacheable = false;
                  }
                }
 
                if (cacheable)
                {
                    connection = (PortConnection)_connections[portName];
                }
                if (connection == null || connection.Port.IsDisposed)
                {
                    connection = new PortConnection(IpcPort.Connect(portName, secure, level, timeout));
                    connection.Port.Cacheable = cacheable;
                }
                else
                {
                    // Remove the connection from the cache
                    _connections.Remove(portName); 
                }
            }
            return connection.Port;
        } // GetSocket
 
        public void ReleaseConnection(IpcPort port)
        {
            string portName = port.Name;
            PortConnection connection = (PortConnection)_connections[portName];
            if (port.Cacheable && (connection == null || connection.Port.IsDisposed))
            {
                lock(_connections)
                {
                    _connections[portName] = new PortConnection(port);
                }
            }
            else
            {
                // there should have been a connection, so let's just close
                //   this socket.
                port.Dispose();
            }
        } // ReleasePort
 
        
    } // ConnectionCache
 
}