Skip to content

[API Proposal]: Calling UnmanagedCallersOnly method regardless of GC mode #119142

Description

@DaZombieKiller

Background and motivation

I am using Windows-specific terminology and APIs in this issue, but the broader idea applies to other platforms as well.

To my knowledge it is not possible today to write a "safe" vectored exception handler in C#, as the runtime will crash when an exception occurs in managed code (due to no GC transition occurring):

static void Main()
{
    unsafe
    {
        AddVectoredExceptionHandler(1, &ExceptionHandler);
        *ReadOnlyOrGuardedPage = 10;
    }
}

[UnmanagedCallersOnly]
static unsafe int ExceptionHandler(EXCEPTION_POINTERS* ex)
{
    Console.WriteLine("ExceptionHandler");
    return EXCEPTION_CONTINUE_SEARCH;
}

This will exit with:

Fatal error.
Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code.

It is possible to bypass this by not using UnmanagedCallersOnly and passing a managed function pointer to AddVectoredExceptionHandler, but I don't think I need to explain why this is a bad idea (and it also now has the opposite issue: it is dangerous if an exception occurs in unmanaged code now).

Additionally, if you define your vectored exception handler in native code, you still have the same problems if you end up calling back into managed code (i.e. through a function pointer).

My use case for this is high-performance dirty page tracking in a C# port of an emulator (this technique is known colloquially among emulator developers as "fastmem", and is also seen in software like VirtualBox). In C++ I allocate pages with PAGE_READONLY protection and catch write exceptions in a vectored exception handler. This handler deals with notifying page listeners and changing the protection to PAGE_READWRITE to suppress exceptions until the page needs to be tracked again. Write watching through GetWriteWatch and MEM_WRITE_WATCH is insufficient as it is a "query" API rather than a "notification" API, so the extremely chatty usage patterns that would be required to use it lead to worse performance by several orders of magnitude.

This issue is similar in spirit to #12077, though that one is limited to catching access violation exceptions. I have use cases for catching other types as well (such as invalid instruction exceptions), though they are less relevant.

API Proposal

I propose adding a new ExtendedGCTransition attribute that can be applied to UnmanagedCallersOnly methods, which causes them to support being invoked regardless of the current GC mode:

namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public sealed class ExtendedGCTransitionAttribute : Attribute
{
    public ExtendedGCTransitionAttribute()
    {
    }
}

API Usage

static void Main()
{
    unsafe
    {
        AddVectoredExceptionHandler(1, &ExceptionHandler);
        *ReadOnlyOrGuardedPage = 2;
    }
}

[ExtendedGCTransition]
[UnmanagedCallersOnly]
static unsafe int ExceptionHandler(EXCEPTION_POINTERS* ex)
{
    Console.WriteLine("ExceptionHandler");
    return EXCEPTION_CONTINUE_SEARCH;
}

Alternative Designs

This could be exposed as a CallConv type, but I don't believe this would make sense as it is not related to how the method is invoked. It could also be possible for the runtime itself to expose a low-level exception handling callback, as it already registers its own VEH that it can dispatch from.

Risks

This is not a common scenario, and may be challenging to support in the runtime (I am not familiar with what would be required to support this kind of method).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    • Status
      No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions