Moves ExceptionDispatchInfo to shared location (#21725)
authorMarek Safar <marek.safar@gmail.com>
Mon, 31 Dec 2018 17:56:57 +0000 (18:56 +0100)
committerJan Kotas <jkotas@microsoft.com>
Mon, 31 Dec 2018 17:56:57 +0000 (07:56 -1000)
* Moves ExceptionDispatchInfo to shared location

* Use struct for Exception.DispatchState, avoid TLAs in names, misc cleanup

src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs [new file with mode: 0644]
src/System.Private.CoreLib/src/System/Exception.cs
src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs [deleted file]

index 6f69bca..be9dabd 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\TypeDependencyAttribute.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\Runtime\ExceptionServices\ExceptionDispatchInfo.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\GcSettings.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ArrayWithOffset.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Attributes.cs" />
index 9a84597..7364bb2 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\Consistency.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\CriticalFinalizerObject.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ConstrainedExecution\ReliabilityContractAttribute.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ExceptionServices\ExceptionDispatchInfo.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ExceptionServices\ExceptionNotification.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\ExceptionServices\HandleProcessCorruptedStateExceptionsAttribute.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\BestFitMappingAttribute.cs" />
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs b/src/System.Private.CoreLib/shared/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs
new file mode 100644 (file)
index 0000000..3578d43
--- /dev/null
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Runtime.ExceptionServices
+{
+    // This class defines support for separating the exception dispatch details
+    // (like stack trace, watson buckets, etc) from the actual managed exception
+    // object. This allows us to track error (via the exception object) independent
+    // of the path the error takes.
+    //
+    // This is particularly useful for frameworks that wish to propagate 
+    // exceptions (i.e. errors to be precise) across threads.
+    public sealed class ExceptionDispatchInfo
+    {
+        private readonly Exception _exception;
+        private readonly Exception.DispatchState _dispatchState;
+
+        private ExceptionDispatchInfo(Exception exception)
+        {
+            _exception = exception;
+            _dispatchState = exception.CaptureDispatchState();
+        }
+
+        // This static method is used to create an instance of ExceptionDispatchInfo for
+        // the specified exception object and save all the required details that maybe
+        // needed to be propagated when the exception is "rethrown" on a different thread.
+        public static ExceptionDispatchInfo Capture(Exception source)
+        {
+            if (source == null)
+            {
+                throw new ArgumentNullException(nameof(source));
+            }
+
+            return new ExceptionDispatchInfo(source);
+        }
+
+        // Return the exception object represented by this ExceptionDispatchInfo instance
+        public Exception SourceException
+        {
+            get
+            {
+                return _exception;
+            }
+        }
+
+        // When a framework needs to "Rethrow" an exception on a thread different (but not necessarily so) from
+        // where it was thrown, it should invoke this method against the ExceptionDispatchInfo
+        // created for the exception in question.
+        //
+        // This method will restore the original stack trace and bucketing details before throwing
+        // the exception so that it is easy, from debugging standpoint, to understand what really went wrong on
+        // the original thread.
+        [StackTraceHidden]
+        public void Throw()
+        {
+            // Restore the exception dispatch details before throwing the exception.
+            _exception.RestoreDispatchState(_dispatchState);
+            throw _exception;
+        }
+
+        // Throws the source exception, maintaining the original bucketing details and augmenting
+        // rather than replacing the original stack trace.
+        public static void Throw(Exception source) => Capture(source).Throw();
+    }
+}
index 911b1c5..1bb73b4 100644 (file)
 **
 =============================================================================*/
 
+using System.Collections;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
 namespace System
 {
-    using System;
-    using System.Runtime.InteropServices;
-    using System.Runtime.CompilerServices;
-    using System.Runtime.Serialization;
-    using System.Runtime.Versioning;
-    using System.Diagnostics;
-    using System.Security;
-    using System.IO;
-    using System.Text;
-    using System.Reflection;
-    using System.Collections;
-    using System.Globalization;
-
     [Serializable]
     [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
     public class Exception : ISerializable
     {
-        private void Init()
+        public Exception()
         {
             _message = null;
             _stackTrace = null;
@@ -46,14 +40,9 @@ namespace System
             _ipForWatsonBuckets = UIntPtr.Zero;
         }
 
-        public Exception()
-        {
-            Init();
-        }
-
         public Exception(string message)
+            : this()
         {
-            Init();
             _message = message;
         }
 
@@ -63,8 +52,8 @@ namespace System
         // is thrown
         // 
         public Exception(string message, Exception innerException)
+            : this()
         {
-            Init();
             _message = message;
             _innerException = innerException;
         }
@@ -274,25 +263,18 @@ namespace System
         {
             get
             {
-                return GetTargetSiteInternal();
-            }
-        }
-
+                if (_exceptionMethod != null)
+                {
+                    return _exceptionMethod;
+                }
+                if (_stackTrace == null)
+                {
+                    return null;
+                }
 
-        // this function is provided as a private helper to avoid the security demand
-        private MethodBase GetTargetSiteInternal()
-        {
-            if (_exceptionMethod != null)
-            {
+                _exceptionMethod = GetExceptionMethodFromStackTrace();
                 return _exceptionMethod;
             }
-            if (_stackTrace == null)
-            {
-                return null;
-            }
-            
-            _exceptionMethod = GetExceptionMethodFromStackTrace();
-            return _exceptionMethod;
         }
 
         // Returns the stack trace as a string.  If no stack trace is
@@ -527,32 +509,7 @@ namespace System
         // for a small duration but that sounds reasonable considering
         // such scenarios are going to be extremely rare, where timing
         // matches precisely.
-        [OptionalField]
-        private static object s_EDILock = new object();
-
-        internal UIntPtr IPForWatsonBuckets
-        {
-            get
-            {
-                return _ipForWatsonBuckets;
-            }
-        }
-
-        internal object WatsonBuckets
-        {
-            get
-            {
-                return _watsonBuckets;
-            }
-        }
-
-        internal string RemoteStackTrace
-        {
-            get
-            {
-                return _remoteStackTraceString;
-            }
-        }
+        private static readonly object s_DispatchStateLock = new object();
 
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         private static extern void PrepareForForeignExceptionRaise();
@@ -593,14 +550,9 @@ namespace System
             }
         }
 
-        internal void GetStackTracesDeepCopy(out object currentStackTrace, out object dynamicMethodArray)
-        {
-            GetStackTracesDeepCopy(this, out currentStackTrace, out dynamicMethodArray);
-        }
-
         // This is invoked by ExceptionDispatchInfo.Throw to restore the exception stack trace, corresponding to the original throw of the
         // exception, just before the exception is "rethrown".
-        internal void RestoreExceptionDispatchInfo(System.Runtime.ExceptionServices.ExceptionDispatchInfo exceptionDispatchInfo)
+        internal void RestoreDispatchState(in DispatchState dispatchState)
         {
             bool fCanProcessException = !(IsImmutableAgileException(this));
             // Restore only for non-preallocated exceptions
@@ -622,25 +574,25 @@ namespace System
                     //
                     // Since deep copying can throw on OOM, try to get the copies
                     // outside the lock.
-                    object _stackTraceCopy = (exceptionDispatchInfo.BinaryStackTraceArray == null) ? null : DeepCopyStackTrace(exceptionDispatchInfo.BinaryStackTraceArray);
-                    object _dynamicMethodsCopy = (exceptionDispatchInfo.DynamicMethodArray == null) ? null : DeepCopyDynamicMethods(exceptionDispatchInfo.DynamicMethodArray);
+                    object _stackTraceCopy = (dispatchState.StackTrace == null) ? null : DeepCopyStackTrace(dispatchState.StackTrace);
+                    object _dynamicMethodsCopy = (dispatchState.DynamicMethods == null) ? null : DeepCopyDynamicMethods(dispatchState.DynamicMethods);
 
                     // Finally, restore the information. 
                     //
                     // Since EDI can be created at various points during exception dispatch (e.g. at various frames on the stack) for the same exception instance,
                     // they can have different data to be restored. Thus, to ensure atomicity of restoration from each EDI, perform the restore under a lock.
-                    lock (Exception.s_EDILock)
+                    lock (s_DispatchStateLock)
                     {
-                        _watsonBuckets = exceptionDispatchInfo.WatsonBuckets;
-                        _ipForWatsonBuckets = exceptionDispatchInfo.IPForWatsonBuckets;
-                        _remoteStackTraceString = exceptionDispatchInfo.RemoteStackTrace;
+                        _watsonBuckets = dispatchState.WatsonBuckets;
+                        _ipForWatsonBuckets = dispatchState.IpForWatsonBuckets;
+                        _remoteStackTraceString = dispatchState.RemoteStackTrace;
                         SaveStackTracesFromDeepCopy(this, _stackTraceCopy, _dynamicMethodsCopy);
                     }
                     _stackTraceString = null;
 
                     // Marks the TES state to indicate we have restored foreign exception
                     // dispatch information.
-                    Exception.PrepareForForeignExceptionRaise();
+                    PrepareForForeignExceptionRaise();
                 }
             }
         }
@@ -745,6 +697,36 @@ namespace System
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
         private static extern void GetMessageFromNativeResources(ExceptionMessageKind kind, StringHandleOnStack retMesg);
+
+        internal readonly struct DispatchState
+        {
+            public readonly object StackTrace;
+            public readonly object DynamicMethods;
+            public readonly string RemoteStackTrace;
+            public readonly UIntPtr IpForWatsonBuckets;
+            public readonly object WatsonBuckets;
+
+            public DispatchState(
+                object stackTrace,
+                object dynamicMethods,
+                string remoteStackTrace,
+                UIntPtr ipForWatsonBuckets,
+                object watsonBuckets)
+            {
+                StackTrace = stackTrace;
+                DynamicMethods = dynamicMethods;
+                RemoteStackTrace = remoteStackTrace;
+                IpForWatsonBuckets = ipForWatsonBuckets;
+                WatsonBuckets = watsonBuckets;
+            }
+        }
+
+        internal DispatchState CaptureDispatchState()
+        {
+            GetStackTracesDeepCopy(this, out object stackTrace, out object dynamicMethods);
+
+            return new DispatchState(stackTrace, dynamicMethods,
+                _remoteStackTraceString, _ipForWatsonBuckets, _watsonBuckets);
+        }
     }
 }
-
diff --git a/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs b/src/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs
deleted file mode 100644 (file)
index ad8e47b..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-/*=============================================================================
-**
-**
-**
-** Purpose: Contains common usage support entities for advanced exception
-**          handling/processing scenarios.
-**
-** Created: 11/2/2010
-** 
-** 
-** 
-=============================================================================*/
-
-using System;
-using System.Diagnostics;
-
-namespace System.Runtime.ExceptionServices
-{
-    // This class defines support for seperating the exception dispatch details
-    // (like stack trace, watson buckets, etc) from the actual managed exception
-    // object. This allows us to track error (via the exception object) independent
-    // of the path the error takes.
-    //
-    // This is particularly useful for frameworks like PFX, APM, etc that wish to
-    // propagate exceptions (i.e. errors to be precise) across threads.
-    public sealed class ExceptionDispatchInfo
-    {
-        // Private members that will hold the relevant details.
-        private Exception m_Exception;
-        private string m_remoteStackTrace;
-        private object m_stackTrace;
-        private object m_dynamicMethods;
-        private UIntPtr m_IPForWatsonBuckets;
-        private object m_WatsonBuckets;
-
-        private ExceptionDispatchInfo(Exception exception)
-        {
-            // Copy over the details we need to save.
-            m_Exception = exception;
-            m_remoteStackTrace = exception.RemoteStackTrace;
-
-            // NOTE: don't be tempted to pass the fields for the out params; the containing object
-            //       might be relocated during the call so the pointers will no longer be valid.
-            object stackTrace;
-            object dynamicMethods;
-            m_Exception.GetStackTracesDeepCopy(out stackTrace, out dynamicMethods);
-            m_stackTrace = stackTrace;
-            m_dynamicMethods = dynamicMethods;
-
-            m_IPForWatsonBuckets = exception.IPForWatsonBuckets;
-            m_WatsonBuckets = exception.WatsonBuckets;
-        }
-
-        internal UIntPtr IPForWatsonBuckets
-        {
-            get
-            {
-                return m_IPForWatsonBuckets;
-            }
-        }
-
-        internal object WatsonBuckets
-        {
-            get
-            {
-                return m_WatsonBuckets;
-            }
-        }
-
-        internal object BinaryStackTraceArray
-        {
-            get
-            {
-                return m_stackTrace;
-            }
-        }
-
-        internal object DynamicMethodArray
-        {
-            get
-            {
-                return m_dynamicMethods;
-            }
-        }
-
-        internal string RemoteStackTrace
-        {
-            get
-            {
-                return m_remoteStackTrace;
-            }
-        }
-
-        // This static method is used to create an instance of ExceptionDispatchInfo for
-        // the specified exception object and save all the required details that maybe
-        // needed to be propagated when the exception is "rethrown" on a different thread.
-        public static ExceptionDispatchInfo Capture(Exception source)
-        {
-            if (source == null)
-            {
-                throw new ArgumentNullException(nameof(source), SR.ArgumentNull_Obj);
-            }
-
-            return new ExceptionDispatchInfo(source);
-        }
-
-        // Return the exception object represented by this ExceptionDispatchInfo instance
-        public Exception SourceException
-        {
-            get
-            {
-                return m_Exception;
-            }
-        }
-
-        // When a framework needs to "Rethrow" an exception on a thread different (but not necessarily so) from
-        // where it was thrown, it should invoke this method against the ExceptionDispatchInfo (EDI)
-        // created for the exception in question.
-        //
-        // This method will restore the original stack trace and bucketing details before throwing
-        // the exception so that it is easy, from debugging standpoint, to understand what really went wrong on
-        // the original thread.
-        [StackTraceHidden]
-        public void Throw()
-        {
-            // Restore the exception dispatch details before throwing the exception.
-            m_Exception.RestoreExceptionDispatchInfo(this);
-            throw m_Exception;
-        }
-
-        // Throws the source exception, maintaining the original bucketing details and augmenting
-        // rather than replacing the original stack trace.
-        public static void Throw(Exception source) => Capture(source).Throw();
-    }
-}