//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ 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; } } }