File: Util\ActivityIdHelper.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="ActivityIdHelper.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Web.Util {
    using System;
    using System.Diagnostics.Tracing;
    using System.Runtime.CompilerServices;
    using System.Threading;
 
    internal sealed class ActivityIdHelper {
 
        private delegate Guid GetCurrentDelegate();
        private delegate void SetAndDestroyDelegate(Guid activityId);
        private delegate void SetAndPreserveDelegate(Guid activityId, out Guid oldActivityThatWillContinue);
 
        // Note to callers: this field can be null.
        internal static readonly ActivityIdHelper Instance = GetSingleton();
 
        private static readonly Guid _baseGuid = Guid.NewGuid();
        private static long _counter;
 
        private readonly GetCurrentDelegate _getCurrentDel;
        private readonly SetAndDestroyDelegate _setAndDestroyDel;
        private readonly SetAndPreserveDelegate _setAndPreserveDel;
 
        // use the factory to create an instance of this type
        private ActivityIdHelper(GetCurrentDelegate getCurrentDel, SetAndDestroyDelegate setAndDestroyDel, SetAndPreserveDelegate setAndPreserveDel) {
            _getCurrentDel = getCurrentDel;
            _setAndDestroyDel = setAndDestroyDel;
            _setAndPreserveDel = setAndPreserveDel;
        }
 
        // Gets the current thread's activity ID.
        public Guid CurrentThreadActivityId {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get { return _getCurrentDel(); }
        }
 
        private static ActivityIdHelper GetSingleton() {
            try {
                // The mscorlib APIs we depend on weren't added until Blue, so we can't
                // take a direct dependency. Need to light up instead.
 
                var getCurrentDel = (GetCurrentDelegate)Delegate.CreateDelegate(
                    typeof(GetCurrentDelegate), typeof(EventSource), "get_CurrentThreadActivityId", ignoreCase: false, throwOnBindFailure: false);
 
                var setAndDestroyDel = (SetAndDestroyDelegate)Delegate.CreateDelegate(
                    typeof(SetAndDestroyDelegate), typeof(EventSource), "SetCurrentThreadActivityId", ignoreCase: false, throwOnBindFailure: false);
 
                var setAndPreserveDel = (SetAndPreserveDelegate)Delegate.CreateDelegate(
                    typeof(SetAndPreserveDelegate), typeof(EventSource), "SetCurrentThreadActivityId", ignoreCase: false, throwOnBindFailure: false);
 
                if (getCurrentDel != null && setAndDestroyDel != null && setAndPreserveDel != null) {
                    return new ActivityIdHelper(getCurrentDel, setAndDestroyDel, setAndPreserveDel);
                }
            }
            catch {
                // exceptions are not fatal; we just won't be able to call the new APIs
            }
 
            return null;
        }
 
        // Disposes of the thread's existing activity ID, then sets the new activity ID on this thread.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SetCurrentThreadActivityId(Guid activityId) {
            _setAndDestroyDel(activityId);
        }
 
        // Suspends (but does not dispose of) the thread's existing activity ID, then sets a new activity ID on this thread.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void SetCurrentThreadActivityId(Guid activityId, out Guid oldActivityThatWillContinue) {
            _setAndPreserveDel(activityId, out oldActivityThatWillContinue);
        }
 
        // !! SECURITY WARNING !!
        // The GUIDs created by this method are predictable and should be used ONLY for tracing.
        // Any other use (such as leaking them to the user) constitutes information disclosure.
        //
        // This is a perf-sensitive method since it could potentially be called many times per
        // request. Guid.NewGuid() is slow since it eventually calls CAPI, and we did actually
        // see it show up as a bottleneck when developing MVC 2. The below implementation has
        // measurably better performance characteristics than calling the other Guid ctors.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe Guid UnsafeCreateNewActivityId() {
            Guid guidCopy = _baseGuid;
            *(long*)(&guidCopy) ^= Interlocked.Increment(ref _counter); // operate on the copy, not the original
            return guidCopy;
        }
 
    }
}