SharedPerformanceCounter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Services / Monitoring / system / Diagnosticts / SharedPerformanceCounter.cs / 3 / SharedPerformanceCounter.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Diagnostics { 
    using System; 
    using System.Text;
    using System.Threading; 
    using System.Collections;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices; 
    using System.Security.Permissions;
    using System.Security; 
    using Microsoft.Win32; 
    using Microsoft.Win32.SafeHandles;
    using System.Globalization; 
    using System.Security.Principal;
    using System.Security.AccessControl;
    using System.Collections.Generic;
    using System.Runtime.Versioning; 

    [HostProtection(Synchronization=true, SharedState=true)] 
    internal sealed class SharedPerformanceCounter { 
        private const int MaxSpinCount = 5000;
        internal const int DefaultCountersFileMappingSize = 524288; 
        internal const int MaxCountersFileMappingSize = 33554432;
        internal const int MinCountersFileMappingSize = 32768;
        internal const int InstanceNameMaxLength = 127;
        internal const int InstanceNameSlotSize = 256; 
        internal const string SingleInstanceName = "systemdiagnosticssharedsingleinstance";
        internal const string DefaultFileMappingName = "netfxcustomperfcounters.1.0"; 
        internal static readonly int SingleInstanceHashCode = GetWstrHashCode(SingleInstanceName); 
        private static Hashtable categoryDataTable = new Hashtable(StringComparer.Ordinal);
        private static readonly int CategoryEntrySize = Marshal.SizeOf(typeof(CategoryEntry)); 
        private static readonly int InstanceEntrySize = Marshal.SizeOf(typeof(InstanceEntry));
        private static readonly int CounterEntrySize = Marshal.SizeOf(typeof(CounterEntry));
        private static readonly int ProcessLifetimeEntrySize = Marshal.SizeOf(typeof(ProcessLifetimeEntry));
 
        private static long LastInstanceLifetimeSweepTick;
        private static long InstanceLifetimeSweepWindow = 30*10000000; //ticks 
        private static ProcessData procData; 

        private static ProcessData ProcessData { 
            get {
                if (procData == null) {
                    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
                    try { 
                        int pid = NativeMethods.GetCurrentProcessId();
                        long startTime = -1; 
 
                        // Though we have asserted the required CAS permissions above, we may
                        // still fail to query the process information if the user does not 
                        // have the necessary process access rights or privileges.
                        // This might be the case if the current process was started by a
                        // different user (primary token) than the current user
                        // (impersonation token) that has less privilege/ACL rights. 
                        using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, false, pid)) {
                            if (!procHandle.IsInvalid) { 
                                long temp; 
                                NativeMethods.GetProcessTimes(procHandle, out startTime, out temp, out temp, out temp);
                            } 
                        }
                        procData = new ProcessData(pid, startTime);
                    }
                    finally  { 
                        SecurityPermission.RevertAssert();
                    } 
                } 
                return procData;
            } 
        }

        // InitialOffset is the offset in our global shared memory where we put the first CategoryEntry.  It needs to be 4 because in
        // v1.0 and v1.1 we used IntPtr.Size.  That creates potential side-by-side issues on 64 bit machines using WOW64. 
        // A v1.0 app running on WOW64 will assume the InitialOffset is 4.  A true 64 bit app on the same machine will assume
        // the initial offset is 8. 
        // However, using an offset of 4 means that our CounterEntry.Value is potentially misaligned.  This is why we have SetValue 
        // and other methods which split CounterEntry.Value into two ints.  With separate shared memory blocks per
        // category, we can fix this and always use an inital offset of 8. 
        internal int InitialOffset = 4;

        private CategoryData categoryData;
        private long baseAddress; 
        private unsafe CounterEntry* counterEntryPointer;
        private string categoryName; 
        private int categoryNameHashCode; 
        private int thisInstanceOffset = -1;
 
        internal SharedPerformanceCounter(string catName, string counterName, string instanceName) :
            this (catName, counterName, instanceName, PerformanceCounterInstanceLifetime.Global) { }

        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        internal unsafe SharedPerformanceCounter(string catName, string counterName, string instanceName, PerformanceCounterInstanceLifetime lifetime) { 
            this.categoryName = catName; 
            this.categoryNameHashCode = GetWstrHashCode(categoryName);
 
            categoryData = GetCategoryData();

            // Check that the instance name isn't too long if we're using the new shared memory.
            // We allocate InstanceNameSlotSize bytes in the shared memory 
            if (categoryData.UseUniqueSharedMemory) {
                if (instanceName != null && instanceName.Length > InstanceNameMaxLength) 
                    throw new InvalidOperationException(SR.GetString(SR.InstanceNameTooLong)); 
            }
            else { 
                if (lifetime != PerformanceCounterInstanceLifetime.Global)
                    throw new InvalidOperationException(SR.GetString(SR.ProcessLifetimeNotValidInGlobal));
            }
 
            if (counterName != null && instanceName != null) {
                if (!categoryData.CounterNames.Contains(counterName)) 
                    Debug.Assert(false, "Counter " + counterName + " does not exist in category " + catName); 
                else
                    this.counterEntryPointer = GetCounter(counterName, instanceName, categoryData.EnableReuse, lifetime); 
            }
        }

        private FileMapping FileView { 
            [ResourceExposure(ResourceScope.Machine)]
            get { 
                return categoryData.FileMapping; 
            }
        } 

        internal unsafe long Value {
            get {
                if (counterEntryPointer == null) 
                    return 0;
 
                return GetValue(this.counterEntryPointer); 
            }
 
            set {
                if (counterEntryPointer == null)
                    return;
 
                SetValue(this.counterEntryPointer, value);
            } 
        } 

        private unsafe int CalculateAndAllocateMemory(int totalSize, out int alignmentAdjustment) { 
            int newOffset;
            int oldOffset;
            alignmentAdjustment = 0;
 
            Debug.Assert(!categoryData.UseUniqueSharedMemory, "We should never be calling CalculateAndAllocateMemory in the unique shared memory");
 
            do { 
                oldOffset = *((int *) baseAddress);
                // we need to verify the oldOffset before we start using it.  Otherwise someone could change 
                // it to something bogus and we would write outside of the shared memory.
                ResolveOffset(oldOffset, 0);

                newOffset = CalculateMemory(oldOffset, totalSize, out alignmentAdjustment); 

                // In the default shared mem we need to make sure that the end address is also aligned.  This is because 
                // in v1.1/v1.0 we just assumed that the next free offset was always properly aligned. 
                int endAddressMod8 = (int) (baseAddress + newOffset) & 0x7;
                int endAlignmentAdjustment = (8 - endAddressMod8) & 0x7; 
                newOffset += endAlignmentAdjustment;

            } while (SafeNativeMethods.InterlockedCompareExchange((IntPtr)baseAddress, newOffset, oldOffset) != oldOffset);
 
            return oldOffset;
        } 
 
        private int CalculateMemory(int oldOffset, int totalSize, out int alignmentAdjustment) {
            int newOffset = CalculateMemoryNoBoundsCheck(oldOffset, totalSize, out alignmentAdjustment); 

            if (newOffset > FileView.FileMappingSize || newOffset < 0) {
                throw new InvalidOperationException(SR.GetString(SR.CountersOOM));
            } 

            return newOffset; 
        } 

        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private int CalculateMemoryNoBoundsCheck(int oldOffset, int totalSize, out int alignmentAdjustment) {
            int currentTotalSize = totalSize;
 
            Thread.MemoryBarrier();
 
            // make sure the start address is 8 byte aligned 
            int startAddressMod8 = (int) (baseAddress + oldOffset) & 0x7;
            alignmentAdjustment = (8 - startAddressMod8) & 0x7; 
            currentTotalSize = currentTotalSize + alignmentAdjustment;

            int newOffset = oldOffset + currentTotalSize;
 
            return newOffset;
        } 
 
        private unsafe int CreateCategory(CategoryEntry* lastCategoryPointer,
                                            int instanceNameHashCode, string instanceName, 
                                            PerformanceCounterInstanceLifetime lifetime) {
            int categoryNameLength;
            int instanceNameLength;
            int alignmentAdjustment; 
            int freeMemoryOffset;
            int newOffset = 0; 
            int totalSize; 

            categoryNameLength = (categoryName.Length + 1) * 2; 
            totalSize = CategoryEntrySize + InstanceEntrySize + (CounterEntrySize * categoryData.CounterNames.Count) + categoryNameLength;
            for (int i=0; iIsConsistent = 0;
            }
            else { 
                instanceNameLength = (instanceName.Length +1) * 2;
                totalSize += instanceNameLength; 
                freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment); 
            }
 
            long nextPtr = ResolveOffset(freeMemoryOffset, totalSize + alignmentAdjustment);

            CategoryEntry* newCategoryEntryPointer;
            InstanceEntry* newInstanceEntryPointer; 
            // We need to decide where to put the padding returned in alignmentAdjustment.  There are several things that
            // need to be aligned.  First, we need to align each struct on a 4 byte boundary so we can use interlocked 
            // operations on the int Spinlock field.  Second, we need to align the CounterEntry on an 8 byte boundary so that 
            // on 64 bit platforms we can use interlocked operations on the Value field.  alignmentAdjustment guarantees 8 byte
            // alignemnt, so we use that for both.  If we're creating the very first category, however, we can't move that 
            // CategoryEntry.  In this case we put the alignmentAdjustment before the InstanceEntry.
            if (freeMemoryOffset == InitialOffset) {
                newCategoryEntryPointer = (CategoryEntry*) nextPtr;
                nextPtr += CategoryEntrySize + alignmentAdjustment; 
                newInstanceEntryPointer = (InstanceEntry*) nextPtr;
            } 
            else { 
                nextPtr += alignmentAdjustment;
                newCategoryEntryPointer = (CategoryEntry*) nextPtr; 
                nextPtr += CategoryEntrySize;
                newInstanceEntryPointer = (InstanceEntry*) nextPtr;
            }
            nextPtr += InstanceEntrySize; 

            // create the first CounterEntry and reserve space for all of the rest.  We won't 
            // finish creating them until the end 
            CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr;
            nextPtr += CounterEntrySize * categoryData.CounterNames.Count; 

            if (categoryData.UseUniqueSharedMemory) {
                ProcessLifetimeEntry* newLifetimeEntry = (ProcessLifetimeEntry*) nextPtr;
                nextPtr += ProcessLifetimeEntrySize; 

                newCounterEntryPointer->LifetimeOffset = (int)((long)newLifetimeEntry - baseAddress); 
                PopulateLifetimeEntry(newLifetimeEntry, lifetime); 
            }
 
            newCategoryEntryPointer->CategoryNameHashCode = categoryNameHashCode;
            newCategoryEntryPointer->NextCategoryOffset = 0;
            newCategoryEntryPointer->FirstInstanceOffset = (int)((long)newInstanceEntryPointer - baseAddress);
            newCategoryEntryPointer->CategoryNameOffset = (int) (nextPtr - baseAddress); 
            Marshal.Copy(categoryName.ToCharArray(), 0, (IntPtr) nextPtr, categoryName.Length);
            nextPtr += categoryNameLength; 
 
            newInstanceEntryPointer->InstanceNameHashCode = instanceNameHashCode;
            newInstanceEntryPointer->NextInstanceOffset = 0; 
            newInstanceEntryPointer->FirstCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
            newInstanceEntryPointer->RefCount = 1;
            newInstanceEntryPointer->InstanceNameOffset = (int) (nextPtr - baseAddress);
            Marshal.Copy(instanceName.ToCharArray(), 0, (IntPtr) nextPtr, instanceName.Length); 
            nextPtr += instanceNameLength;
 
            string counterName = (string) categoryData.CounterNames[0]; 
            newCounterEntryPointer->CounterNameHashCode = GetWstrHashCode(counterName);
            SetValue(newCounterEntryPointer, 0); 
            newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
            Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length);
            nextPtr += (counterName.Length + 1) * 2;
 
            CounterEntry* previousCounterEntryPointer;
            for (int i=1; iCounterNameHashCode = GetWstrHashCode(counterName);
                SetValue(newCounterEntryPointer, 0);
                newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress); 
                Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length);
 
                nextPtr += (counterName.Length + 1) * 2; 
                previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
            } 

            Debug.Assert(nextPtr - baseAddress == freeMemoryOffset + totalSize + alignmentAdjustment, "We should have used all of the space we requested at this point");

            int offset = (int) ((long) newCategoryEntryPointer - baseAddress); 
            lastCategoryPointer->IsConsistent = 0;
            // If not the first category node, link it. 
            if (offset != InitialOffset) 
                lastCategoryPointer->NextCategoryOffset = offset;
 
            if (categoryData.UseUniqueSharedMemory) {
                *((int*) baseAddress) = newOffset;
                lastCategoryPointer->IsConsistent = 1;
            } 
            return offset;
        } 
 
        private unsafe int CreateInstance(CategoryEntry* categoryPointer,
                                            int instanceNameHashCode, string instanceName, 
                                            PerformanceCounterInstanceLifetime lifetime) {
            int instanceNameLength;
            int totalSize = InstanceEntrySize +  (CounterEntrySize * categoryData.CounterNames.Count);
            int alignmentAdjustment; 
            int freeMemoryOffset;
            int newOffset = 0; 
 

            if (categoryData.UseUniqueSharedMemory) { 
                instanceNameLength = InstanceNameSlotSize;
                totalSize += ProcessLifetimeEntrySize + instanceNameLength;

                // If we're in a separate shared memory, we need to do a two stage update of the free memory pointer. 
                // First we calculate our alignment adjustment and where the new free offset is.  Then we
                // write the new structs and data.  The last two operations are to link the new structs into the 
                // existing ones and update the next free offset.  Our process could get killed in between those two, 
                // leaving the memory in an inconsistent state.  We use the "IsConsistent" flag to help determine
                // when that has happened. 
                freeMemoryOffset = *((int *) baseAddress);
                newOffset = CalculateMemory(freeMemoryOffset, totalSize, out alignmentAdjustment);
            }
            else { 
                instanceNameLength = (instanceName.Length +1) * 2;
                totalSize += instanceNameLength; 
 
                // add in the counter names for the global shared mem.
                for (int i=0; iLifetimeOffset = (int)((long)newLifetimeEntry - baseAddress); 
                PopulateLifetimeEntry(newLifetimeEntry, lifetime);
            } 
 
            // set up the InstanceEntry
            newInstanceEntryPointer->InstanceNameHashCode = instanceNameHashCode; 
            newInstanceEntryPointer->NextInstanceOffset = 0;
            newInstanceEntryPointer->FirstCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
            newInstanceEntryPointer->RefCount = 1;
            newInstanceEntryPointer->InstanceNameOffset = (int) (nextPtr - baseAddress); 
            Marshal.Copy(instanceName.ToCharArray(), 0, (IntPtr) nextPtr, instanceName.Length);
            nextPtr += instanceNameLength; 
 

            if (categoryData.UseUniqueSharedMemory) { 
                // in the unique shared mem we'll assume that the CounterEntries of the first instance
                // are all created.  Then we can just refer to the old counter name rather than copying in a new one.
                InstanceEntry* firstInstanceInCategoryPointer = (InstanceEntry*) ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize);
                CounterEntry* firstCounterInCategoryPointer = (CounterEntry*) ResolveOffset(firstInstanceInCategoryPointer->FirstCounterOffset, CounterEntrySize); 
                newCounterEntryPointer->CounterNameHashCode = firstCounterInCategoryPointer->CounterNameHashCode;
                SetValue(newCounterEntryPointer, 0); 
                newCounterEntryPointer->CounterNameOffset = firstCounterInCategoryPointer->CounterNameOffset; 

                // now create the rest of the CounterEntrys 
                CounterEntry* previousCounterEntryPointer;
                for (int i=1; iNextCounterOffset != 0, "The unique shared memory should have all of its counters created by the time we hit CreateInstance"); 
                    firstCounterInCategoryPointer = (CounterEntry*) ResolveOffset(firstCounterInCategoryPointer->NextCounterOffset, CounterEntrySize); 
                    newCounterEntryPointer->CounterNameHashCode = firstCounterInCategoryPointer->CounterNameHashCode;
                    SetValue(newCounterEntryPointer, 0); 
                    newCounterEntryPointer->CounterNameOffset = firstCounterInCategoryPointer->CounterNameOffset;

                    previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);
                } 
            }
            else { 
                // now create the rest of the CounterEntrys 
                CounterEntry* previousCounterEntryPointer = null;
                for (int i=0; iCounterNameHashCode = GetWstrHashCode(counterName);
                    newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
                    Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length); 
                    nextPtr += (counterName.Length + 1) * 2;
 
                    SetValue(newCounterEntryPointer, 0); 

                    if (i != 0) 
                        previousCounterEntryPointer->NextCounterOffset = (int)((long)newCounterEntryPointer - baseAddress);

                    previousCounterEntryPointer = newCounterEntryPointer;
                    newCounterEntryPointer++; 
                }
            } 
 
            Debug.Assert(nextPtr - baseAddress == freeMemoryOffset + totalSize, "We should have used all of the space we requested at this point");
 
            int offset = (int) ((long) newInstanceEntryPointer - baseAddress);
            categoryPointer->IsConsistent = 0;

            // prepend the new instance rather than append, helps with perf of hooking up subsequent counters 
            newInstanceEntryPointer->NextInstanceOffset = categoryPointer->FirstInstanceOffset;
            categoryPointer->FirstInstanceOffset = offset; 
 
            if (categoryData.UseUniqueSharedMemory) {
                *((int*) baseAddress) = newOffset; 
                categoryPointer->IsConsistent = 1;
            }

            return freeMemoryOffset; 
        }
 
        private unsafe int CreateCounter(CounterEntry* lastCounterPointer, 
                                           int counterNameHashCode, string counterName) {
            int counterNameLength = (counterName.Length + 1) * 2; 
            int totalSize = sizeof(CounterEntry) + counterNameLength;
            int alignmentAdjustment;
            int freeMemoryOffset;
 
            Debug.Assert(!categoryData.UseUniqueSharedMemory, "We should never be calling CreateCounter in the unique shared memory");
            freeMemoryOffset = CalculateAndAllocateMemory(totalSize, out alignmentAdjustment); 
 
            freeMemoryOffset += alignmentAdjustment;
 
            long nextPtr = ResolveOffset(freeMemoryOffset, totalSize);
            CounterEntry* newCounterEntryPointer = (CounterEntry*) nextPtr;
            nextPtr += sizeof(CounterEntry);
 
            newCounterEntryPointer->CounterNameOffset = (int) (nextPtr - baseAddress);
            newCounterEntryPointer->CounterNameHashCode = counterNameHashCode; 
            newCounterEntryPointer->NextCounterOffset = 0; 
            SetValue(newCounterEntryPointer, 0);
            Marshal.Copy(counterName.ToCharArray(), 0, (IntPtr) nextPtr, counterName.Length); 

            Debug.Assert(nextPtr + counterNameLength - baseAddress == freeMemoryOffset + totalSize, "We should have used all of the space we requested at this point");

            lastCounterPointer->NextCounterOffset = (int) ((long) newCounterEntryPointer - baseAddress); 
            return freeMemoryOffset;
        } 
 

        private unsafe static void PopulateLifetimeEntry(ProcessLifetimeEntry *lifetimeEntry, PerformanceCounterInstanceLifetime lifetime) { 

            if (lifetime == PerformanceCounterInstanceLifetime.Process) {

 
                lifetimeEntry->LifetimeType = (int) PerformanceCounterInstanceLifetime.Process;
                lifetimeEntry->ProcessId = ProcessData.ProcessId; 
                lifetimeEntry->StartupTime = ProcessData.StartupTime; 
            }
            else { 
                lifetimeEntry->ProcessId = 0;
                lifetimeEntry->StartupTime = 0;
            }
        } 

 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
        private static unsafe void WaitAndEnterCriticalSection(int* spinLockPointer, out bool taken) {
            WaitForCriticalSection(spinLockPointer); 

            // Note - we are taking a lock here, but it probably isn't
            // worthwhile to use Thread.BeginCriticalRegion & EndCriticalRegion.
            // These only really help the CLR escalate from a thread abort 
            // to an appdomain unload, under the assumption that you may be
            // editing shared state within the appdomain.  Here you are editing 
            // shared state, but it is shared across processes.  Unloading the 
            // appdomain isn't exactly helping.  The only thing that would help
            // would be if the CLR tells the host to ensure all allocations 
            // have a higher chance of succeeding within this critical region,
            // but of course that's only a probabilisitic statement.

            // Must be able to assign to the out param. 
            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
            } 
            finally {
                int r = Interlocked.CompareExchange(ref *spinLockPointer, 1, 0); 
                taken = (r == 0);
            }
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        private static unsafe void WaitForCriticalSection(int* spinLockPointer) { 
            int spinCount = MaxSpinCount; 
            for (; spinCount > 0 && *spinLockPointer != 0; spinCount--) {
                // We suspect there are scenarios where the finalizer thread 
                // will call this method.  The finalizer thread runs with
                // a higher priority than the other code.  Using SpinWait
                // isn't sufficient, since it only spins, but doesn't yield
                // to any lower-priority threads.  Call Thread.Sleep(1). 
                if (*spinLockPointer != 0)
                    Thread.Sleep(1); 
            } 

            // if the lock still isn't free, most likely there's a deadlock caused by a process 
            // getting killed while it held the lock.  We'll just free the lock
            if (spinCount == 0 && *spinLockPointer != 0)
                *spinLockPointer = 0;
        } 

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        private static unsafe void ExitCriticalSection(int* spinLockPointer) { 
            *spinLockPointer = 0;
        } 

        // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
        // This hashcode function is identical to the one in SharedPerformanceCounter.cpp.  If
        // you change one without changing the other, perfcounters will break. 
        internal static int GetWstrHashCode(string wstr)
        { 
            uint hash = 5381; 
            for(uint i=0; i < wstr.Length; i++)
                hash = ((hash << 5) + hash) ^ wstr[(int) i]; 
            return (int)hash;
        }

        // Calculate the length of a string in the shared memory.  If we reach the end of the shared memory 
        // before we see a null terminator, we throw.
        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private unsafe int GetStringLength(char* startChar) {
            char* currentChar = startChar; 
            ulong endAddress = (ulong) (baseAddress + FileView.FileMappingSize);

            while((ulong) currentChar < (endAddress - 2)) {
                if (*currentChar == 0) 
                    return (int) (currentChar - startChar);
 
                currentChar++; 
            }
 
            throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
        }

        // Compare a managed string to a string located at a given offset.  If we walk past the end of the 
        // shared memory, we throw.
        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private unsafe bool StringEquals(string stringA, int offset) {
            char* currentChar = (char*) ResolveOffset(offset, 0); 
            ulong endAddress = (ulong) (baseAddress + FileView.FileMappingSize);

            int i;
            for (i=0; i (endAddress - 2))
                    throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted)); 
 
                if (stringA[i] != currentChar[i])
                    return false; 
            }

            // now check for the null termination.
            if ((ulong) (currentChar+i) > (endAddress - 2)) 
                throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
 
            return (currentChar[i] == 0); 
        }
 

        [ResourceExposure(ResourceScope.Machine)]  // Memory maps the perf counter data file
        [ResourceConsumption(ResourceScope.Machine)]
        private unsafe CategoryData GetCategoryData() { 
            CategoryData data = (CategoryData) categoryDataTable[categoryName];
 
            if (data == null) { 
                lock(categoryDataTable) {
                    data = (CategoryData) categoryDataTable[categoryName]; 
                    if (data == null) {
                        data = new CategoryData();
                        data.FileMappingName = DefaultFileMappingName;
                        data.MutexName = categoryName; 

                        RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); 
                        registryPermission.Assert(); 
                        RegistryKey categoryKey = null;
                        try { 
                            categoryKey = Registry.LocalMachine.OpenSubKey(PerformanceCounterLib.ServicePath + "\\" + categoryName + "\\Performance");

                            // first read the options
                            Object optionsObject = categoryKey.GetValue("CategoryOptions"); 
                            if (optionsObject != null) {
                                int options = (int) optionsObject; 
                                data.EnableReuse = (((PerformanceCounterCategoryOptions) options & PerformanceCounterCategoryOptions.EnableReuse) != 0); 

                                if (((PerformanceCounterCategoryOptions) options & PerformanceCounterCategoryOptions.UseUniqueSharedMemory) != 0) { 
                                    data.UseUniqueSharedMemory = true;
                                    InitialOffset = 8;
                                    data.FileMappingName = DefaultFileMappingName + categoryName;
                                } 
                            }
 
                            int fileMappingSize; 
                            object fileMappingSizeObject = categoryKey.GetValue("FileMappingSize");
                            if (fileMappingSizeObject != null && data.UseUniqueSharedMemory) { 
                                // we only use this reg value in the unique shared memory case.
                                fileMappingSize = (int) fileMappingSizeObject;
                                if (fileMappingSize < MinCountersFileMappingSize)
                                    fileMappingSize = MinCountersFileMappingSize; 

                                if (fileMappingSize > MaxCountersFileMappingSize) 
                                    fileMappingSize = MaxCountersFileMappingSize; 
                            }
                            else { 
                                fileMappingSize = GetFileMappingSizeFromConfig();
                                if (data.UseUniqueSharedMemory)
                                    fileMappingSize = fileMappingSize >> 2;  // if we have a custom filemapping, only make it 25% as large.
                            } 

                            // now read the counter names 
                            object counterNamesObject = categoryKey.GetValue("Counter Names"); 
                            byte[] counterNamesBytes = counterNamesObject as byte[];
 
                            if (counterNamesBytes != null) {
                                ArrayList names = new ArrayList();
                                fixed (byte* counterNamesPtr = counterNamesBytes) {
                                    int start = 0; 
                                    for (int i=0; iSpinLock), out sectionEntered);

                    int newCategoryOffset; 
                    if (sectionEntered) {
                        try { 
                            newCategoryOffset = CreateCategory(categoryPointer, instanceNameHashCode, instanceName, lifetime); 
                        }
                        finally { 
                            if (!categoryData.UseUniqueSharedMemory)
                                ExitCriticalSection(&(categoryPointer->SpinLock));
                        }
 
                        categoryPointer = (CategoryEntry*)(ResolveOffset(newCategoryOffset, CategoryEntrySize));
                        instancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); 
                        counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer); 
                        Debug.Assert(counterFound, "All counters should be created, so we should always find the counter");
                        return counterPointer; 
                    }
                }

                bool foundFreeInstance; 
                while (!FindInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, true, lifetime, out foundFreeInstance)) {
                    InstanceEntry* lockInstancePointer = instancePointer; 
 
                    // don't bother locking again if we're using a separate shared memory.
                    bool sectionEntered; 
                    if (categoryData.UseUniqueSharedMemory)
                        sectionEntered = true;
                    else
                        WaitAndEnterCriticalSection(&(lockInstancePointer->SpinLock), out sectionEntered); 

                    if (sectionEntered) { 
                        try { 
                            bool reused = false;
 
                            if (enableReuse && foundFreeInstance) {
                                reused = TryReuseInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, lifetime, lockInstancePointer);
                                // at this point we might have reused an instance that came from v1.1/v1.0.  We can't assume it will have the counter
                                // we're looking for. 
                            }
 
                            if (!reused) { 
                                int newInstanceOffset = CreateInstance(categoryPointer, instanceNameHashCode, instanceName, lifetime);
                                instancePointer = (InstanceEntry*)(ResolveOffset(newInstanceOffset, InstanceEntrySize)); 

                                counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer);
                                Debug.Assert(counterFound, "All counters should be created, so we should always find the counter");
                                return counterPointer; 
                            }
                        } 
                        finally { 
                            if (!categoryData.UseUniqueSharedMemory)
                                ExitCriticalSection(&(lockInstancePointer->SpinLock)); 
                        }
                    }
                }
 
                if (categoryData.UseUniqueSharedMemory) {
                    counterFound = FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer); 
                    Debug.Assert(counterFound, "All counters should be created, so we should always find the counter"); 
                    return counterPointer;
                } 
                else {
                    while (!FindCounter(counterNameHashCode, counterName, instancePointer, &counterPointer)) {
                        bool sectionEntered;
                        WaitAndEnterCriticalSection(&(counterPointer->SpinLock), out sectionEntered); 

                        if (sectionEntered) { 
                            try { 
                                int newCounterOffset =  CreateCounter(counterPointer, counterNameHashCode, counterName);
                                return (CounterEntry*) (ResolveOffset(newCounterOffset, CounterEntrySize)); 
                            }
                            finally {
                                ExitCriticalSection(&(counterPointer->SpinLock));
                            } 
                        }
                    } 
 
                    return counterPointer;
                } 
            }
            finally {
                // cache this instance for reuse
                try { 
                    if (counterPointer != null && instancePointer != null) {
                        this.thisInstanceOffset = ResolveAddress((long)instancePointer, InstanceEntrySize); 
                    } 
                }
                catch (InvalidOperationException) { 
                    this.thisInstanceOffset = -1;
                }

                if (mutex != null) { 
                    mutex.ReleaseMutex();
                    mutex.Close(); 
                } 
            }
        } 

        private unsafe bool FindCategory(CategoryEntry** returnCategoryPointerReference) {
            CategoryEntry* firstCategoryPointer =  (CategoryEntry*)(ResolveOffset(InitialOffset, CategoryEntrySize));
 
            CategoryEntry* currentCategoryPointer = firstCategoryPointer;
            CategoryEntry* previousCategoryPointer = firstCategoryPointer; 
            for(;;) { 
                if (currentCategoryPointer->IsConsistent == 0)
                    Verify(currentCategoryPointer); 

                if (currentCategoryPointer->CategoryNameHashCode == categoryNameHashCode) {
                    if (StringEquals(categoryName,  currentCategoryPointer->CategoryNameOffset)) {
                        *returnCategoryPointerReference = currentCategoryPointer; 
                        return true;
                    } 
                } 

                previousCategoryPointer = currentCategoryPointer; 
                if (currentCategoryPointer->NextCategoryOffset != 0)
                    currentCategoryPointer = (CategoryEntry*)(ResolveOffset(currentCategoryPointer->NextCategoryOffset, CategoryEntrySize));
                else {
                    *returnCategoryPointerReference = previousCategoryPointer; 
                    return false;
                } 
            } 
        }
 
        private unsafe bool FindCounter(int counterNameHashCode, string counterName, InstanceEntry* instancePointer, CounterEntry** returnCounterPointerReference) {
            CounterEntry* currentCounterPointer = (CounterEntry*)(ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize));
            CounterEntry* previousCounterPointer = currentCounterPointer;
            for(;;) { 
                if (currentCounterPointer->CounterNameHashCode == counterNameHashCode) {
                    if (StringEquals(counterName, currentCounterPointer->CounterNameOffset)) { 
                        *returnCounterPointerReference = currentCounterPointer; 
                        return true;
                    } 
                }

                previousCounterPointer = currentCounterPointer;
                if (currentCounterPointer->NextCounterOffset != 0) 
                    currentCounterPointer = (CounterEntry*)(ResolveOffset(currentCounterPointer->NextCounterOffset, CounterEntrySize));
                else { 
                    *returnCounterPointerReference = previousCounterPointer; 
                    return false;
                } 
            }
        }

        private unsafe bool FindInstance(int instanceNameHashCode, string instanceName, 
                                           CategoryEntry* categoryPointer, InstanceEntry** returnInstancePointerReference,
                                           bool activateUnusedInstances, PerformanceCounterInstanceLifetime lifetime, 
                                           out bool foundFreeInstance) { 

            InstanceEntry* currentInstancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); 
            InstanceEntry* previousInstancePointer = currentInstancePointer;
            foundFreeInstance = false;
            // Look at the first instance to determine if this is single or multi instance.
            if (currentInstancePointer->InstanceNameHashCode == SingleInstanceHashCode) { 
                if (StringEquals(SingleInstanceName, currentInstancePointer->InstanceNameOffset)){
                    if (instanceName != SingleInstanceName) 
                        throw new InvalidOperationException(SR.GetString(SR.SingleInstanceOnly, categoryName)); 
                }
                else { 
                    if (instanceName == SingleInstanceName)
                        throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, categoryName));
                }
            } 
            else {
                if (instanceName == SingleInstanceName) 
                    throw new InvalidOperationException(SR.GetString(SR.MultiInstanceOnly, categoryName)); 
            }
 
            //
            // 1st pass find exact matching!
            //
            // We don't need to aggressively claim unused instances. For performance, we would proactively 
            // verify lifetime of instances if activateUnusedInstances is specified and certain time
            // has elapsed since last sweep or we are running out of shared memory. 
            bool verifyLifeTime = activateUnusedInstances; 
            if (activateUnusedInstances) {
 
                int totalSize = InstanceEntrySize + ProcessLifetimeEntrySize + InstanceNameSlotSize +  (CounterEntrySize * categoryData.CounterNames.Count);
                int freeMemoryOffset = *((int *) baseAddress);
                int alignmentAdjustment;
                int newOffset = CalculateMemoryNoBoundsCheck(freeMemoryOffset, totalSize, out alignmentAdjustment); 

                if (!(newOffset > FileView.FileMappingSize || newOffset < 0)) { 
                    long tickDelta = (DateTime.Now.Ticks - LastInstanceLifetimeSweepTick); 
                    if (tickDelta < InstanceLifetimeSweepWindow)
                        verifyLifeTime = false; 
                }
            }

            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); 
            try {
                for(;;) { 
                    bool verifiedLifetimeOfThisInstance = false; 
                    if (verifyLifeTime && (currentInstancePointer->RefCount != 0)) {
                        verifiedLifetimeOfThisInstance = true; 
                        VerifyLifetime(currentInstancePointer);
                    }

                    if (currentInstancePointer->InstanceNameHashCode == instanceNameHashCode) { 
                        if (StringEquals(instanceName, currentInstancePointer->InstanceNameOffset)){
                            // we found a matching instance. 
                            *returnInstancePointerReference = currentInstancePointer; 

                            CounterEntry* firstCounter = (CounterEntry*) ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize); 
                            ProcessLifetimeEntry* lifetimeEntry;
                            if (categoryData.UseUniqueSharedMemory)
                                lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(firstCounter->LifetimeOffset, ProcessLifetimeEntrySize);
                            else 
                                lifetimeEntry = null;
 
                            // ensure that we have verified the lifetime of the matched instance 
                            if (!verifiedLifetimeOfThisInstance && currentInstancePointer->RefCount != 0)
                                VerifyLifetime(currentInstancePointer); 

                            if (currentInstancePointer->RefCount != 0) {
                                if (lifetimeEntry != null && lifetimeEntry->ProcessId != 0) {
                                    if (lifetime != PerformanceCounterInstanceLifetime.Process) 
                                        throw new InvalidOperationException(SR.GetString(SR.CantConvertProcessToGlobal));
 
                                    // make sure only one process is using this instance. 
                                    if (ProcessData.ProcessId != lifetimeEntry->ProcessId)
                                        throw new InvalidOperationException(SR.GetString(SR.InstanceAlreadyExists, instanceName)); 

                                   // compare start time of the process, account for ACL issues in querying process information
                                    if ((lifetimeEntry->StartupTime != -1) && (ProcessData.StartupTime != -1)) {
                                        if (ProcessData.StartupTime != lifetimeEntry->StartupTime) 
                                            throw new InvalidOperationException(SR.GetString(SR.InstanceAlreadyExists, instanceName));
                                    } 
                                } 
                                else {
                                    if (lifetime == PerformanceCounterInstanceLifetime.Process) 
                                        throw new InvalidOperationException(SR.GetString(SR.CantConvertGlobalToProcess));
                                }
                                return true;
                            } 

                            if (activateUnusedInstances) { 
                                Mutex mutex = null; 
                                RuntimeHelpers.PrepareConstrainedRegions();
                                try { 
                                    SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);
                                    ClearCounterValues(currentInstancePointer);
                                    if (lifetimeEntry != null)
                                        PopulateLifetimeEntry(lifetimeEntry, lifetime); 

                                    currentInstancePointer->RefCount = 1; 
                                    return true; 
                                }
                                finally { 
                                    if (mutex != null) {
                                        mutex.ReleaseMutex();
                                        mutex.Close();
                                    } 
                                }
                            } 
                            else 
                                return false;
                        } 
                    }

                    if (currentInstancePointer->RefCount == 0) {
                        foundFreeInstance = true; 
                    }
 
                    previousInstancePointer = currentInstancePointer; 
                    if (currentInstancePointer->NextInstanceOffset != 0)
                        currentInstancePointer =  (InstanceEntry*)(ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize)); 
                    else {
                        *returnInstancePointerReference = previousInstancePointer;
                        return false;
                    } 
                }
            } 
            finally  { 
                SecurityPermission.RevertAssert();
 
                if (verifyLifeTime)
                    LastInstanceLifetimeSweepTick = DateTime.Now.Ticks;
            }
        } 

        private unsafe bool TryReuseInstance(int instanceNameHashCode, string instanceName, 
                                               CategoryEntry* categoryPointer, InstanceEntry** returnInstancePointerReference, 
                                               PerformanceCounterInstanceLifetime lifetime,
                                               InstanceEntry* lockInstancePointer) { 
            //
            // 2nd pass find a free instance slot
            //
            InstanceEntry* currentInstancePointer = (InstanceEntry*)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); 
            InstanceEntry* previousInstancePointer = currentInstancePointer;
            for (;;) { 
                if (currentInstancePointer->RefCount == 0) { 

                    bool hasFit; 
                    long instanceNamePtr;       // we need cache this to avoid race conditions.

                    if (categoryData.UseUniqueSharedMemory) {
                        instanceNamePtr = ResolveOffset(currentInstancePointer->InstanceNameOffset, InstanceNameSlotSize); 
                        // In the separate shared memory case we should always have enough space for instances.  The
                        // name slot size is fixed. 
                        Debug.Assert(((instanceName.Length + 1) * 2) <= InstanceNameSlotSize, "The instance name length should always fit in our slot size"); 
                        hasFit = true;
                    } 
                    else {
                        // we don't know the string length yet.
                        instanceNamePtr = ResolveOffset(currentInstancePointer->InstanceNameOffset, 0);
 
                        // In the global shared memory, we require names to be exactly the same length in order
                        // to reuse them.  This way we don't end up leaking any space and we don't need to 
                        // depend on the layout of the memory to calculate the space we have. 
                        int length = GetStringLength((char*) instanceNamePtr);
                        hasFit = (length == instanceName.Length); 
                    }

                    bool noSpinLock = (lockInstancePointer == currentInstancePointer) || categoryData.UseUniqueSharedMemory;
                    // Instance name fit 
                    if (hasFit) {
                        // don't bother locking again if we're using a separate shared memory. 
                        bool sectionEntered; 
                        if (noSpinLock)
                            sectionEntered = true; 
                        else
                            WaitAndEnterCriticalSection(&(currentInstancePointer->SpinLock), out sectionEntered);

                        if (sectionEntered) { 
                            try {
                                // Make copy with zero-term 
                                char[] tmp = new char[instanceName.Length + 1]; 
                                instanceName.CopyTo(0, tmp, 0, instanceName.Length);
                                tmp[instanceName.Length] = '\0'; 
                                Marshal.Copy(tmp, 0, (IntPtr) instanceNamePtr, tmp.Length);
                                currentInstancePointer->InstanceNameHashCode = instanceNameHashCode;

                                // return 
                                *returnInstancePointerReference = currentInstancePointer;
                                // clear the counter values. 
                                ClearCounterValues(*returnInstancePointerReference); 

                                if (categoryData.UseUniqueSharedMemory) { 
                                    CounterEntry* counterPointer = (CounterEntry*)ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize);
                                    ProcessLifetimeEntry* lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(counterPointer->LifetimeOffset, ProcessLifetimeEntrySize);
                                    PopulateLifetimeEntry(lifetimeEntry, lifetime);
                                } 

                                (*returnInstancePointerReference)->RefCount = 1; 
                                return true; 
                            }
                            finally { 
                                if (!noSpinLock)
                                    ExitCriticalSection(&(currentInstancePointer->SpinLock));
                            }
                        } 
                    }
                 } 
 
                previousInstancePointer = currentInstancePointer;
                if (currentInstancePointer->NextInstanceOffset != 0) 
                    currentInstancePointer = (InstanceEntry*)(ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize));
                else
                {
                    *returnInstancePointerReference = previousInstancePointer; 
                    return false;
                } 
            } 
        }
 
        private unsafe void Verify(CategoryEntry* currentCategoryPointer) {
            if (!categoryData.UseUniqueSharedMemory)
                return;
 
            Mutex mutex = null;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try { 
                SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);
                VerifyCategory(currentCategoryPointer); 
            }
            finally {
                if (mutex != null) {
                    mutex.ReleaseMutex(); 
                    mutex.Close();
                } 
            } 
         }
 
        private unsafe void VerifyCategory(CategoryEntry* currentCategoryPointer) {
            int freeOffset = *((int*)baseAddress);
            ResolveOffset(freeOffset, 0);        // verify next free offset
 
            if (currentCategoryPointer->NextCategoryOffset > freeOffset)
                currentCategoryPointer->NextCategoryOffset = 0; 
            else if (currentCategoryPointer->NextCategoryOffset != 0) 
                VerifyCategory((CategoryEntry*) ResolveOffset(currentCategoryPointer->NextCategoryOffset, CategoryEntrySize));
 
            if (currentCategoryPointer->FirstInstanceOffset != 0) {
                // In V3, we started prepending the new instances rather than appending (as in V2) for performance.
                // Check whether the recently added instance at the head of the list is committed. If not, rewire
                // the head of the list to point to the next instance 
                if (currentCategoryPointer->FirstInstanceOffset > freeOffset) {
                    InstanceEntry* currentInstancePointer = (InstanceEntry*) ResolveOffset(currentCategoryPointer->FirstInstanceOffset, InstanceEntrySize); 
                    currentCategoryPointer->FirstInstanceOffset = currentInstancePointer->NextInstanceOffset; 
                    if (currentCategoryPointer->FirstInstanceOffset > freeOffset)
                        currentCategoryPointer->FirstInstanceOffset = 0; 
                }
                //

 

 
                if (currentCategoryPointer->FirstInstanceOffset != 0) { 
                    Debug.Assert(currentCategoryPointer->FirstInstanceOffset <= freeOffset, "The head of the list is inconsistent - possible mismatch of V2 & V3 instances?");
                    VerifyInstance((InstanceEntry*) ResolveOffset(currentCategoryPointer->FirstInstanceOffset, InstanceEntrySize)); 
                }
            }

            currentCategoryPointer->IsConsistent = 1; 
        }
 
        private unsafe void VerifyInstance(InstanceEntry* currentInstancePointer) { 
            int freeOffset = *((int*)baseAddress);
            ResolveOffset(freeOffset, 0);        // verify next free offset 

            if (currentInstancePointer->NextInstanceOffset > freeOffset)
                currentInstancePointer->NextInstanceOffset = 0;
            else if (currentInstancePointer->NextInstanceOffset != 0) 
                VerifyInstance((InstanceEntry*) ResolveOffset(currentInstancePointer->NextInstanceOffset, InstanceEntrySize));
        } 
 
        private unsafe void VerifyLifetime(InstanceEntry* currentInstancePointer) {
            Debug.Assert(currentInstancePointer->RefCount != 0, "RefCount must be 1 for instances passed to VerifyLifetime"); 

            CounterEntry* counter = (CounterEntry*) ResolveOffset(currentInstancePointer->FirstCounterOffset, CounterEntrySize);
            if (counter->LifetimeOffset != 0) {
                ProcessLifetimeEntry* lifetime = (ProcessLifetimeEntry*) ResolveOffset(counter->LifetimeOffset, ProcessLifetimeEntrySize); 
                if (lifetime->LifetimeType == (int) PerformanceCounterInstanceLifetime.Process) {
                    int pid = lifetime->ProcessId; 
                    long startTime = lifetime->StartupTime; 

                    if (pid != 0) { 

                        // Optimize for this process
                        if (pid == ProcessData.ProcessId) {
                            if ((ProcessData.StartupTime != -1) && (startTime != -1) && (ProcessData.StartupTime != startTime)) { 
                                // Process id got recycled.  Reclaim this instance.
                                currentInstancePointer->RefCount = 0; 
                                return; 
                            }
                        } 
                        else {
                            long processStartTime;
                            using (SafeProcessHandle procHandle = SafeProcessHandle.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION, false, pid)) {
                                int error = Marshal.GetLastWin32Error(); 
                                if ((error == NativeMethods.ERROR_INVALID_PARAMETER) && procHandle.IsInvalid) {
                                    // The process is dead.  Reclaim this instance.  Note that we only clear the refcount here. 
                                    // If we tried to clear the pid and startup time as well, we would have a race where 
                                    // we could clear the pid/startup time but not the refcount.
                                    currentInstancePointer->RefCount = 0; 
                                    return;
                                }

                                // Defer cleaning the instance when we had previously encountered errors in 
                                // recording process start time (i.e, when startTime == -1) until after the
                                // process id is not valid (which will be caught in the if check above) 
                                if (!procHandle.IsInvalid && startTime != -1) { 
                                    long temp;
                                    if (NativeMethods.GetProcessTimes(procHandle, out processStartTime, out temp, out temp, out temp)) { 
                                        if (processStartTime != startTime) {
                                            // The process is dead but a new one is using the same pid.  Reclaim this instance.
                                            currentInstancePointer->RefCount = 0;
                                            return; 
                                        }
                                    } 
                                } 
                            }
                        } 
                    }

                }
            } 
        }
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
        internal unsafe long IncrementBy(long value) {
            if (counterEntryPointer == null) 
                return 0;

            CounterEntry* counterEntry = this.counterEntryPointer;
 
            return AddToValue(counterEntry, value);
        } 
 
        internal unsafe long Increment() {
            if (counterEntryPointer == null) 
                return 0;

            return IncrementUnaligned(this.counterEntryPointer);
        } 

        internal unsafe long Decrement() { 
            if (counterEntryPointer == null) 
                return 0;
 
            return DecrementUnaligned(this.counterEntryPointer);
        }

        internal unsafe static void RemoveAllInstances(string categoryName) { 
            SharedPerformanceCounter spc = new SharedPerformanceCounter(categoryName, null, null);
            spc.RemoveAllInstances(); 
            RemoveCategoryData(categoryName); 
        }
 
        private unsafe void RemoveAllInstances() {
            CategoryEntry* categoryPointer;
            if (!FindCategory(&categoryPointer))
                return; 

            InstanceEntry* instancePointer = (InstanceEntry *)(ResolveOffset(categoryPointer->FirstInstanceOffset, InstanceEntrySize)); 
 
            Mutex mutex = null;
            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {
                SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);
                for(;;) {
                    RemoveOneInstance(instancePointer, true); 

                    if (instancePointer->NextInstanceOffset != 0) 
                        instancePointer =  (InstanceEntry*)(ResolveOffset(instancePointer->NextInstanceOffset, InstanceEntrySize)); 
                    else {
                        break; 
                    }
                }
            }
            finally { 
                if (mutex != null) {
                    mutex.ReleaseMutex(); 
                    mutex.Close(); 
                }
            } 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        internal unsafe void RemoveInstance(string instanceName, PerformanceCounterInstanceLifetime instanceLifetime) { 
             if (instanceName == null || instanceName.Length == 0)
                return; 
 
            int instanceNameHashCode = GetWstrHashCode(instanceName);
 
            CategoryEntry* categoryPointer;
            if (!FindCategory(&categoryPointer))
                return;
 
            InstanceEntry* instancePointer = null;
            bool validatedCachedInstancePointer = false; 
            bool temp; 

            Mutex mutex = null; 
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
                SharedUtils.EnterMutexWithoutGlobal(categoryData.MutexName, ref mutex);
 
                if (this.thisInstanceOffset != -1) {
                    try { 
                        // validate whether the cached instance pointer is pointing at the right instance 
                        instancePointer = (InstanceEntry*)(ResolveOffset(this.thisInstanceOffset, InstanceEntrySize));
                        if (instancePointer->InstanceNameHashCode == instanceNameHashCode) { 
                            if (StringEquals(instanceName, instancePointer->InstanceNameOffset)){
                                validatedCachedInstancePointer = true;

                                // this is probably overkill 
                                CounterEntry* firstCounter = (CounterEntry*) ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize);
                                ProcessLifetimeEntry* lifetimeEntry; 
                                if (categoryData.UseUniqueSharedMemory) { 
                                    lifetimeEntry = (ProcessLifetimeEntry*) ResolveOffset(firstCounter->LifetimeOffset, ProcessLifetimeEntrySize);
                                    if (lifetimeEntry != null 
                                        && lifetimeEntry->LifetimeType == (int)PerformanceCounterInstanceLifetime.Process
                                        && lifetimeEntry->ProcessId != 0) {
                                        validatedCachedInstancePointer &= (instanceLifetime == PerformanceCounterInstanceLifetime.Process);
                                        validatedCachedInstancePointer &= (ProcessData.ProcessId == lifetimeEntry->ProcessId); 
                                        if ((lifetimeEntry->StartupTime != -1) && (ProcessData.StartupTime != -1))
                                            validatedCachedInstancePointer &= (ProcessData.StartupTime == lifetimeEntry->StartupTime); 
                                    } 
                                    else
                                        validatedCachedInstancePointer &= (instanceLifetime != PerformanceCounterInstanceLifetime.Process); 
                                }
                            }
                        }
                    } 
                    catch (InvalidOperationException) {
                        validatedCachedInstancePointer = false; 
                    } 
                    if (!validatedCachedInstancePointer)
                        this.thisInstanceOffset = -1; 
                }

                if (!validatedCachedInstancePointer && !FindInstance(instanceNameHashCode, instanceName, categoryPointer, &instancePointer, false, instanceLifetime, out temp))
                    return ; 

                if (instancePointer != null) 
                    RemoveOneInstance(instancePointer, false); 
            }
            finally { 
                if (mutex != null) {
                    mutex.ReleaseMutex();
                    mutex.Close();
                } 
            }
        } 
 
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        private unsafe void RemoveOneInstance(InstanceEntry* instancePointer, bool clearValue) { 
            bool sectionEntered = false;

            RuntimeHelpers.PrepareConstrainedRegions();
            try { 
                if (!categoryData.UseUniqueSharedMemory) {
                    while (!sectionEntered) { 
                        WaitAndEnterCriticalSection(&(instancePointer->SpinLock), out sectionEntered); 
                    }
                } 

                instancePointer->RefCount = 0;

                if (clearValue) 
                    ClearCounterValues(instancePointer);
            } 
            finally { 
                if (sectionEntered)
                    ExitCriticalSection(&(instancePointer->SpinLock)); 
            }
        }

        private unsafe void ClearCounterValues(InstanceEntry* instancePointer) { 
            //Clear counter instance values
            CounterEntry* currentCounterPointer = null; 
 
            if (instancePointer->FirstCounterOffset != 0)
                currentCounterPointer = (CounterEntry*)(ResolveOffset(instancePointer->FirstCounterOffset, CounterEntrySize)); 

            while(currentCounterPointer != null) {
                SetValue(currentCounterPointer, 0);
 
                if (currentCounterPointer->NextCounterOffset != 0)
                    currentCounterPointer = (CounterEntry*)(ResolveOffset(currentCounterPointer->NextCounterOffset, CounterEntrySize)); 
                else 
                    currentCounterPointer = null;
            } 

        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
        private static unsafe long AddToValue(CounterEntry* counterEntry, long addend) {
            // Called while holding a lock - shouldn't have to worry about 
            // reading misaligned data & getting old vs. new parts of an Int64. 
            if (IsMisaligned(counterEntry)) {
                ulong newvalue; 

                CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry;
                newvalue = (uint)entry->Value_hi;
                newvalue <<= 32; 
                newvalue |= (uint)entry->Value_lo;
 
 
                newvalue = (ulong) ((long) newvalue + addend);
 
                entry->Value_hi = (int) (newvalue >> 32);
                entry->Value_lo = (int) (newvalue & 0xffffffff);

                return (long) newvalue; 
            }
            else 
                return Interlocked.Add(ref counterEntry->Value, addend); 
        }
 
        private static unsafe long DecrementUnaligned(CounterEntry* counterEntry) {
            if (IsMisaligned(counterEntry))
                return AddToValue(counterEntry, -1);
            else 
                return Interlocked.Decrement(ref counterEntry->Value);
        } 
 
        private static unsafe long GetValue(CounterEntry* counterEntry) {
            if (IsMisaligned(counterEntry)) { 
                ulong value;
                CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry;
                value = (uint)entry->Value_hi;
                value <<= 32; 
                value |= (uint)entry->Value_lo;
 
                return (long) value; 
            }
            else 
                return counterEntry->Value;
        }

        private static unsafe long IncrementUnaligned(CounterEntry* counterEntry) { 
            if (IsMisaligned(counterEntry))
                return AddToValue(counterEntry, 1); 
            else 
                return Interlocked.Increment(ref counterEntry->Value);
        } 

        private static unsafe void SetValue(CounterEntry* counterEntry, long value) {
            if (IsMisaligned(counterEntry)) {
                CounterEntryMisaligned* entry = (CounterEntryMisaligned*) counterEntry; 
                entry->Value_lo = (int) (value & 0xffffffff);
                entry->Value_hi = (int) (value >> 32); 
            } 
            else
                counterEntry->Value = value; 
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        private static unsafe bool IsMisaligned(CounterEntry* counterEntry) { 
            return (( (Int64)counterEntry & 0x7) != 0);
        } 
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private long ResolveOffset(int offset, int sizeToRead) {
            //It is very important to check the integrity of the shared memory
            //everytime a new address is resolved.
            if (offset > (FileView.FileMappingSize - sizeToRead) || offset < 0) 
                throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));
 
            long address = baseAddress + offset; 

            return address; 
        }

        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 
        private int ResolveAddress(long address, int sizeToRead) {
            int offset = (int)(address - baseAddress); 
 
            //It is very important to check the integrity of the shared memory
            //everytime a new address is resolved. 
            if (offset > (FileView.FileMappingSize - sizeToRead) || offset < 0)
                throw new InvalidOperationException(SR.GetString(SR.MappingCorrupted));

            return offset; 
        }
 
        private class FileMapping { 
            internal int FileMappingSize;
            private SafeFileMapViewHandle fileViewAddress = null; 
            private SafeFileMappingHandle fileMappingHandle = null;
            //The version of the file mapping name is independent from the
            //assembly version.
 
            public FileMapping(string fileMappingName, int fileMappingSize, int initialOffset) {
                this.Initialize(fileMappingName, fileMappingSize, initialOffset); 
            } 

            internal IntPtr FileViewAddress { 
                get {
                    if (fileViewAddress.IsInvalid)
                       throw new InvalidOperationException(SR.GetString(SR.SharedMemoryGhosted));
 
                    return fileViewAddress.DangerousGetHandle();
                } 
            } 

            private unsafe void Initialize(string fileMappingName, int fileMappingSize, int initialOffset) { 
                string mappingName = fileMappingName;
                SharedUtils.CheckEnvironment();

                SafeLocalMemHandle securityDescriptorPointer = null; 
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
                try { 
                    // The sddl string consists of these parts: 
                    // D:           it's a DACL
                    // (A;          this is an allow ACE 
                    // OICI;        object inherit and container inherit
                    // FRFWGRGW;;;  allow file read, file write, generic read and generic write
                    // AU)          granted to Authenticated Users
                    // ;S-1-5-33)   the same permission granted to AU is also granted to restricted services 
                    string sddlString = "D:(A;OICI;FRFWGRGW;;;AU)(A;OICI;FRFWGRGW;;;S-1-5-33)";
 
                    if (!SafeLocalMemHandle.ConvertStringSecurityDescriptorToSecurityDescriptor(sddlString, NativeMethods.SDDL_REVISION_1, 
                                                                                                    out securityDescriptorPointer, IntPtr.Zero))
                        throw new InvalidOperationException(SR.GetString(SR.SetSecurityDescriptorFailed)); 

                    NativeMethods.SECURITY_ATTRIBUTES securityAttributes = new NativeMethods.SECURITY_ATTRIBUTES();
                    securityAttributes.lpSecurityDescriptor = securityDescriptorPointer;
                    securityAttributes.bInheritHandle = false; 

                    bool created = false; 
                    while (!created) { 
                        fileMappingHandle = NativeMethods.CreateFileMapping((IntPtr)(-1), securityAttributes,
                                                                  NativeMethods.PAGE_READWRITE, 0, fileMappingSize, mappingName); 

                        if ((Marshal.GetLastWin32Error() != NativeMethods.ERROR_ACCESS_DENIED) || !fileMappingHandle.IsInvalid) {
                            created = true;
                        } 
                        else {
                            // Invalidate the old safehandle before we get rid of it.  This prevents it from trying to finalize 
                            fileMappingHandle.SetHandleAsInvalid(); 
                            fileMappingHandle = NativeMethods.OpenFileMapping(NativeMethods.FILE_MAP_WRITE, false, mappingName);
 
                            if ((Marshal.GetLastWin32Error() != NativeMethods.ERROR_FILE_NOT_FOUND) || !fileMappingHandle.IsInvalid)
                                created = true;
                        }
                    } 
                    if (fileMappingHandle.IsInvalid) {
                        throw new InvalidOperationException(SR.GetString(SR.CantCreateFileMapping)); 
                    } 

                    fileViewAddress = SafeFileMapViewHandle.MapViewOfFile(fileMappingHandle, NativeMethods.FILE_MAP_WRITE, 0,0, UIntPtr.Zero); 
                    if (fileViewAddress.IsInvalid)
                        throw new InvalidOperationException(SR.GetString(SR.CantMapFileView));

                    // figure out what size the share memory really is. 
                    NativeMethods.MEMORY_BASIC_INFORMATION meminfo = new NativeMethods.MEMORY_BASIC_INFORMATION();
                    if (NativeMethods.VirtualQuery(fileViewAddress, ref meminfo, (IntPtr) sizeof(NativeMethods.MEMORY_BASIC_INFORMATION)) == IntPtr.Zero) 
                        throw new InvalidOperationException(SR.GetString(SR.CantGetMappingSize)); 

                    FileMappingSize = (int) meminfo.RegionSize; 
                }
                finally {
                    if (securityDescriptorPointer != null) securityDescriptorPointer.Close();
                    SecurityPermission.RevertAssert(); 
                }
 
                SafeNativeMethods.InterlockedCompareExchange(fileViewAddress.DangerousGetHandle(), initialOffset, 0); 
            }
 
        }

        // 
        // The final tmpPadding field is needed to make the size of this structure 8-byte aligned.  This is 
        // necessary on IA64.
        //  
        // Note that in V1.0 and v1.1 there was no explicit padding defined on any of these structs.  That means that 
        // sizeof(CategoryEntry) or Marshal.SizeOf(typeof(CategoryEntry)) returned 4 bytes less before Whidbey,
        // and the int we use as IsConsistent could actually overlap the InstanceEntry SpinLock. 

        [StructLayout(LayoutKind.Sequential)]
        private struct CategoryEntry {
            public int SpinLock; 
            public int CategoryNameHashCode;
            public int CategoryNameOffset; 
            public int FirstInstanceOffset; 
            public int NextCategoryOffset;
            public int IsConsistent;         // this was 4 bytes of padding in v1.0/v1.1 
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct InstanceEntry { 
            public int SpinLock;
            public int InstanceNameHashCode; 
            public int InstanceNameOffset; 
            public int RefCount;
            public int FirstCounterOffset; 
            public int NextInstanceOffset;
        }

        [StructLayout(LayoutKind.Sequential)] 
        private struct CounterEntry {
            public int SpinLock; 
            public int CounterNameHashCode; 
            public int CounterNameOffset;
            public int LifetimeOffset;          // this was 4 bytes of padding in v1.0/v1.1 
            public long Value;
            public int NextCounterOffset;
            public int padding2;
        } 

        [StructLayout(LayoutKind.Sequential)] 
        private struct CounterEntryMisaligned { 
            public int SpinLock;
            public int CounterNameHashCode; 
            public int CounterNameOffset;
            public int LifetimeOffset;         // this was 4 bytes of padding in v1.0/v1.1
            public int Value_lo;
            public int Value_hi; 
            public int NextCounterOffset;
            public int padding2;        // The compiler adds this only if there is an int64 in the struct - 
                                        // ie only for CounterEntry.  It really needs to be here. 
        }
 
        [StructLayout(LayoutKind.Sequential)]
        private struct ProcessLifetimeEntry {
            public int LifetimeType;
            public int ProcessId; 
            public Int64 StartupTime;
        } 
 
        private class CategoryData {
            public FileMapping FileMapping; 
            public bool EnableReuse;
            public bool UseUniqueSharedMemory;
            public string FileMappingName;
            public string MutexName; 
            public ArrayList CounterNames;
        } 
    } 

    internal class ProcessData { 
        public ProcessData(int pid, long startTime) {
            ProcessId = pid;
            StartupTime = startTime;
        } 
        public int ProcessId;
        public long StartupTime; 
    } 

} 


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK