2015-04-07 09:35:12 +01:00
//------------------------------------------------------------
// 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 ;
}
2016-02-22 11:00:01 -05:00
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 ;
}
2015-04-07 09:35:12 +01:00
// 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 ) ;
2016-02-22 11:00:01 -05:00
string hash = ( ( uint ) StringUtil . GetNonRandomizedHashCode ( str ) % 99 ) . ToString ( "00" , CultureInfo . InvariantCulture ) ;
2015-04-07 09:35:12 +01:00
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 ) ;
}
}
}
}
}
}