File: System\ServiceModel\Diagnostics\PerformanceCountersBase.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Diagnostics
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.PerformanceData;
    using System.Globalization;
    using System.Runtime;
    using System.Threading;
    using System.Linq;
 
    abstract class PerformanceCountersBase : IDisposable
    {
        internal abstract string InstanceName
        {
            get;
        }
 
        internal abstract string[] CounterNames
        {
            get;
        }
 
        internal abstract int PerfCounterStart
        {
            get;
        }
 
        internal abstract int PerfCounterEnd
        {
            get;
        }
 
        private static string GetInstanceNameWithHash(string instanceName, string fullInstanceName)
        {
            return String.Format("{0}{1}", instanceName, StringUtil.GetNonRandomizedHashCode(fullInstanceName).ToString("X", CultureInfo.InvariantCulture));
        }
 
        protected static string EnsureUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)
        {
            if (String.IsNullOrEmpty(categoryName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("categoryName");
            if (String.IsNullOrEmpty(instanceName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("instanceName");
            if (String.IsNullOrEmpty(fullInstanceName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("fullInstanceName");
 
            try
            {
                // If the instance name is already used, append a hash of the full name to it.
                if (PerformanceCounterCategory.InstanceExists(instanceName, categoryName))
                {
                    return GetInstanceNameWithHash(instanceName, fullInstanceName);
                }
            }
            catch 
            { 
                // If an exception is thrown, return the instance name without modification.
            }
 
            return instanceName;
        }
 
        protected static string GetUniqueInstanceName(string categoryName, string instanceName, string fullInstanceName)
        {
            if (String.IsNullOrEmpty(categoryName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("categoryName");
            if (String.IsNullOrEmpty(instanceName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("instanceName");
            if (String.IsNullOrEmpty(fullInstanceName))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNullOrEmptyString("fullInstanceName");
 
            try
            {
                // If the instance name with the hash appended exists, return it.
                string nameWithHash = GetInstanceNameWithHash(instanceName, fullInstanceName);
                if (PerformanceCounterCategory.InstanceExists(nameWithHash, categoryName))
                {
                    return nameWithHash;
                }
            }
            catch
            {
                // If an exception is thrown, return the instance name without modification.
            }
 
            return instanceName;            
        }
 
        // remove count chars from string and add a 2 char hash code to beginning or end, as specified.
        protected static string GetHashedString(string str, int startIndex, int count, bool hashAtEnd)
        {
            string returnVal = str.Remove(startIndex, count);
            string hash = ((uint)StringUtil.GetNonRandomizedHashCode(str) % 99).ToString("00", CultureInfo.InvariantCulture);
            return hashAtEnd ? returnVal + hash : hash + returnVal;
        }
 
        internal abstract bool Initialized { get; }
 
        protected int disposed = 0;
 
        public void Dispose()
        {
            if (Interlocked.Exchange(ref disposed, 1) == 0)
            {
                Dispose(true);
            }
        }
 
        protected virtual void Dispose(bool disposing)
        {
        }
 
        // A CounterSetInstance is not disposed immediately when a service, endpoint or operation perf counter is disposed. Because messages 
        // can be processed while a ServiceHost is being closed, and such messages can try to update perf counters data, resulting in AVs or 
        // corruptions (see bug 249132 @ CSDMain). So instead of disposing a CounterSetInstance, we hold a WeakReference to it, until either 
        // GC reclaims it or a new service/endpoint/operation perf counter is started with the same name (and re-uses the CounterSetInstance).
        // The CounterSetInstance finalizer will free up the perf counters memory, so we don't have a leak.
        protected class CounterSetInstanceCache
        {
            // instance name -> WeakReference of CounterSetInstance
            private readonly Dictionary<string, WeakReference> cache = new Dictionary<string, WeakReference>();
 
            /// <summary>
            /// Returns and removes the CounterSetInstance with the specified name from the cache. Returns null if not found.
            /// </summary>
            internal CounterSetInstance Get(string instanceName)
            {
                Fx.Assert(instanceName != null, "Invalid argument.");
 
                lock (this.cache)
                {
                    WeakReference wr;
                    if (this.cache.TryGetValue(instanceName, out wr))
                    {
                        Fx.Assert(wr != null, "The values in 'availableCounterSetInstances' should not be null.");
                        this.cache.Remove(instanceName);
                        return (CounterSetInstance)wr.Target;
                    }
                    else
                    {
                        return null;
                    }
                }
            }
 
            /// <summary>
            /// Adds a CounterSetInstance to the cache, from where it will be garbage collected or re-used by another performance counter (whichever occurs first).
            /// </summary>
            internal void Add(string instanceName, CounterSetInstance instance)
            {
                Fx.Assert(instanceName != null, "Invalid argument.");
                Fx.Assert(instance != null, "Invalid argument.");
 
                lock (this.cache)
                {
                    this.cache[instanceName] = new WeakReference(instance);
                }
            }
 
            /// <summary>
            /// Clear the entries for CounterSetInstances that were garbage collected.
            /// </summary>
            internal void Cleanup()
            {
                lock (this.cache)
                {
                    foreach (var entry in this.cache.Where(pair => !pair.Value.IsAlive).ToList())
                    {
                        this.cache.Remove(entry.Key);
                    }
                }
            }
        }
    }
}