diff --git a/README.md b/README.md index 9140bb3..ac7a1ba 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ It also includes the following optional runtime-supported polyfills: - `[TargetPlatform]` - `[UnsupportedOSPlatform]` - `[UnsupportedOSPlatformGuard]` +- `[SuppressGCTransition]` (see [here](https://devblogs.microsoft.com/dotnet/improvements-in-native-code-interop-in-net-5-0/)) # Options ⚙️ diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.InteropServices.SuppressGCTransitionAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.InteropServices.SuppressGCTransitionAttribute.cs new file mode 100644 index 0000000..0c01242 --- /dev/null +++ b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.InteropServices.SuppressGCTransitionAttribute.cs @@ -0,0 +1,72 @@ +// +#pragma warning disable +#nullable enable annotations + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.InteropServices +{ + /// + /// An attribute used to indicate a GC transition should be skipped when making an unmanaged function call. + /// + /// + /// Example of a valid use case. The Win32 `GetTickCount()` function is a small performance related function + /// that reads some global memory and returns the value. In this case, the GC transition overhead is significantly + /// more than the memory read. + /// + /// using System; + /// using System.Runtime.InteropServices; + /// class Program + /// { + /// [DllImport("Kernel32")] + /// [SuppressGCTransition] + /// static extern int GetTickCount(); + /// static void Main() + /// { + /// Console.WriteLine($"{GetTickCount()}"); + /// } + /// } + /// + /// + /// + /// This attribute is ignored if applied to a method without the . + /// + /// Forgoing this transition can yield benefits when the cost of the transition is more than the execution time + /// of the unmanaged function. However, avoiding this transition removes some of the guarantees the runtime + /// provides through a normal P/Invoke. When exiting the managed runtime to enter an unmanaged function the + /// GC must transition from Cooperative mode into Preemptive mode. Full details on these modes can be found at + /// https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/clr-code-guide.md#2.1.8. + /// Suppressing the GC transition is an advanced scenario and should not be done without fully understanding + /// potential consequences. + /// + /// One of these consequences is an impact to Mixed-mode debugging (https://docs.microsoft.com/visualstudio/debugger/how-to-debug-in-mixed-mode). + /// During Mixed-mode debugging, it is not possible to step into or set breakpoints in a P/Invoke that + /// has been marked with this attribute. A workaround is to switch to native debugging and set a breakpoint in the native function. + /// In general, usage of this attribute is not recommended if debugging the P/Invoke is important, for example + /// stepping through the native code or diagnosing an exception thrown from the native code. + /// + /// The runtime may load the native library for method marked with this attribute in advance before the method is called for the first time. + /// Usage of this attribute is not recommended for platform neutral libraries with conditional platform specific code. + /// + /// The P/Invoke method that this attribute is applied to must have all of the following properties: + /// * Native function always executes for a trivial amount of time (less than 1 microsecond). + /// * Native function does not perform a blocking syscall (e.g. any type of I/O). + /// * Native function does not call back into the runtime (e.g. Reverse P/Invoke). + /// * Native function does not throw exceptions. + /// * Native function does not manipulate locks or other concurrency primitives. + /// + /// Consequences of invalid uses of this attribute: + /// * GC starvation. + /// * Immediate runtime termination. + /// * Data corruption. + /// + [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false)] + [global::System.Diagnostics.Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")] + internal sealed class SuppressGCTransitionAttribute : global::System.Attribute + { + public SuppressGCTransitionAttribute() + { + } + } +} \ No newline at end of file diff --git a/tests/PolySharp.Tests/RuntimeSupport.cs b/tests/PolySharp.Tests/RuntimeSupport.cs index 0e164cf..ea6ac91 100644 --- a/tests/PolySharp.Tests/RuntimeSupport.cs +++ b/tests/PolySharp.Tests/RuntimeSupport.cs @@ -19,6 +19,7 @@ public void HideMe() internal class PlatformSpecificApis { [UnmanagedCallersOnly] + [SuppressGCTransition] public static void NativeFunction() { }